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 MAX_PAGES = 200;
|
||||
|
||||
// سقف استاندارد هر فایل sitemap ۵۰٬۰۰۰ URL است؛ کمی زودتر هشدار میدهیم.
|
||||
const SITEMAP_INDEX_THRESHOLD = 45000;
|
||||
|
||||
function toSafeDate(value) {
|
||||
if (!value) return null;
|
||||
const d = new Date(typeof value === 'number' ? value * 1000 : value);
|
||||
@@ -106,33 +109,17 @@ function cityFilterParams(scope) {
|
||||
return params;
|
||||
}
|
||||
|
||||
// شهرهایی که دامنهٔ اختصاصی دارند — موجودیتهایشان canonical روی همان دامنه دارند و
|
||||
// نباید در sitemap دامنهٔ اصلی تکرار شوند.
|
||||
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));
|
||||
}
|
||||
|
||||
// پاسخ لیست پزشکان از GET /api/v1/doctors حالا خودش city دارد، پس تفکیک دامنه با همان
|
||||
// فیلتری انجام میشود که کلینیکها استفاده میکنند. پیش از این، نبودِ city مجبورمان
|
||||
// میکرد روی دامنهٔ اصلی کل لیست را ۳۵ بار (یکبار بهازای هر شهرِ دامنهدار) بگیریم و
|
||||
// تفاضل بگیریم — همان کاری که ~۱۳ ثانیه طول میکشید.
|
||||
async function getDoctorUrls(baseUrl, scope) {
|
||||
const doctors = await getDoctorsForScope(scope);
|
||||
const doctors = await fetchAllPages('/api/v1/doctors', cityFilterParams(scope));
|
||||
return doctors
|
||||
.filter((d) => !isThinDoctor(d))
|
||||
// روی دامنهٔ اصلی فقط پزشکانی که شهرشان دامنهٔ اختصاصی ندارد؛ روی دامنهٔ شهری
|
||||
// فیلتر city_id سمت API قبلاً کار را کرده است.
|
||||
.filter((d) => !scope.isRoot || !findDomainByCityId(extractEntityCityId(d)))
|
||||
.map((d) =>
|
||||
withLastModified(
|
||||
{
|
||||
@@ -221,11 +208,24 @@ export default async function sitemap() {
|
||||
];
|
||||
|
||||
const seen = new Set();
|
||||
return allUrls.filter((item) => {
|
||||
const uniqueUrls = allUrls.filter((item) => {
|
||||
if (seen.has(item.url)) return false;
|
||||
seen.add(item.url);
|
||||
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) {
|
||||
console.error('Error generating sitemap:', error);
|
||||
return [];
|
||||
|
||||
Reference in New Issue
Block a user