feat(sitemap): optimize doctor URL fetching and add sitemap index threshold warning
This commit is contained in:
+25
-25
@@ -20,6 +20,9 @@ const API_URL = process.env.NEXT_PUBLIC_API_URL;
|
|||||||
const PAGE_LIMIT = 50;
|
const PAGE_LIMIT = 50;
|
||||||
const MAX_PAGES = 200;
|
const MAX_PAGES = 200;
|
||||||
|
|
||||||
|
// سقف استاندارد هر فایل sitemap ۵۰٬۰۰۰ URL است؛ کمی زودتر هشدار میدهیم.
|
||||||
|
const SITEMAP_INDEX_THRESHOLD = 45000;
|
||||||
|
|
||||||
function toSafeDate(value) {
|
function toSafeDate(value) {
|
||||||
if (!value) return null;
|
if (!value) return null;
|
||||||
const d = new Date(typeof value === 'number' ? value * 1000 : value);
|
const d = new Date(typeof value === 'number' ? value * 1000 : value);
|
||||||
@@ -106,33 +109,17 @@ function cityFilterParams(scope) {
|
|||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
// شهرهایی که دامنهٔ اختصاصی دارند — موجودیتهایشان canonical روی همان دامنه دارند و
|
// پاسخ لیست پزشکان از GET /api/v1/doctors حالا خودش city دارد، پس تفکیک دامنه با همان
|
||||||
// نباید در sitemap دامنهٔ اصلی تکرار شوند.
|
// فیلتری انجام میشود که کلینیکها استفاده میکنند. پیش از این، نبودِ city مجبورمان
|
||||||
const CITY_IDS_WITH_DOMAIN = citiesData
|
// میکرد روی دامنهٔ اصلی کل لیست را ۳۵ بار (یکبار بهازای هر شهرِ دامنهدار) بگیریم و
|
||||||
.filter((c) => !isRootCity(c) && c.domain)
|
// تفاضل بگیریم — همان کاری که ~۱۳ ثانیه طول میکشید.
|
||||||
.map((c) => c.id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* پاسخ لیست پزشکان شهر ندارد؛ پس تفکیک با فیلتر سمت API انجام میشود:
|
|
||||||
* روی دامنهٔ شهری با city_id، و روی دامنهٔ اصلی «همه منهای پزشکانِ شهرهای دامنهدار».
|
|
||||||
*/
|
|
||||||
async function getDoctorsForScope(scope) {
|
|
||||||
if (!scope.isRoot) return fetchAllPages('/api/v1/doctors', cityFilterParams(scope));
|
|
||||||
|
|
||||||
const [all, ...perCity] = await Promise.all([
|
|
||||||
fetchAllPages('/api/v1/doctors'),
|
|
||||||
...CITY_IDS_WITH_DOMAIN.map((cityId) =>
|
|
||||||
fetchAllPages('/api/v1/doctors', { city_id: String(cityId) })
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
const ownedByCityDomain = new Set(perCity.flat().map((d) => d?.uuid).filter(Boolean));
|
|
||||||
return all.filter((d) => !ownedByCityDomain.has(d?.uuid));
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getDoctorUrls(baseUrl, scope) {
|
async function getDoctorUrls(baseUrl, scope) {
|
||||||
const doctors = await getDoctorsForScope(scope);
|
const doctors = await fetchAllPages('/api/v1/doctors', cityFilterParams(scope));
|
||||||
return doctors
|
return doctors
|
||||||
.filter((d) => !isThinDoctor(d))
|
.filter((d) => !isThinDoctor(d))
|
||||||
|
// روی دامنهٔ اصلی فقط پزشکانی که شهرشان دامنهٔ اختصاصی ندارد؛ روی دامنهٔ شهری
|
||||||
|
// فیلتر city_id سمت API قبلاً کار را کرده است.
|
||||||
|
.filter((d) => !scope.isRoot || !findDomainByCityId(extractEntityCityId(d)))
|
||||||
.map((d) =>
|
.map((d) =>
|
||||||
withLastModified(
|
withLastModified(
|
||||||
{
|
{
|
||||||
@@ -221,11 +208,24 @@ export default async function sitemap() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const seen = new Set();
|
const seen = new Set();
|
||||||
return allUrls.filter((item) => {
|
const uniqueUrls = allUrls.filter((item) => {
|
||||||
if (seen.has(item.url)) return false;
|
if (seen.has(item.url)) return false;
|
||||||
seen.add(item.url);
|
seen.add(item.url);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// sitemap-index عمداً پیاده نشده: سقف استاندارد هر فایل ۵۰٬۰۰۰ URL است و
|
||||||
|
// بزرگترین دامنه اکنون حدود ۲٬۵۰۰ URL دارد — یعنی هنوز یک مرتبهٔ بزرگی فاصله.
|
||||||
|
// پیادهسازیاش با generateSitemaps ساختار URL را به /sitemap/[id].xml میبرد و
|
||||||
|
// ارجاع robots.txt هم باید همراهش عوض شود؛ پس تا نزدیکشدن به آستانه نمیارزد.
|
||||||
|
// این هشدار هست تا وقتی واقعاً لازم شد، در لاگ دیده شود نه در Search Console.
|
||||||
|
if (uniqueUrls.length > SITEMAP_INDEX_THRESHOLD) {
|
||||||
|
console.warn(
|
||||||
|
`[sitemap] ${domain}: ${uniqueUrls.length} URLs — نزدیک سقف ۵۰k، وقت پیادهسازی sitemap-index است`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return uniqueUrls;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error generating sitemap:', error);
|
console.error('Error generating sitemap:', error);
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
Reference in New Issue
Block a user