Fix community IDs and update sitemap functionality

- Updated community IDs for various components in graph.json to reflect correct associations.
- Added new function `getPublishedDoctors()` in app/sitemap.js and established relationships with existing functions.
- Adjusted source locations for several functions in app/sitemap.js to ensure accurate mapping.
- Enhanced canonical URL handling in lib/getCanonicalUrl.js to prevent incorrect canonicalization for paginated specialty pages.
- Updated manifest.json with new modification times and AST hashes for affected files.
This commit is contained in:
hamed
2026-07-19 09:32:00 +03:30
parent 97e990982f
commit 672a7d89bf
8 changed files with 332 additions and 304 deletions
+50 -23
View File
@@ -113,23 +113,28 @@ function cityFilterParams(scope) {
// فیلتری انجام می‌شود که کلینیک‌ها استفاده می‌کنند. پیش از این، نبودِ city مجبورمان
// می‌کرد روی دامنهٔ اصلی کل لیست را ۳۵ بار (یک‌بار به‌ازای هر شهرِ دامنه‌دار) بگیریم و
// تفاضل بگیریم — همان کاری که ~۱۳ ثانیه طول می‌کشید.
async function getDoctorUrls(baseUrl, scope) {
// پزشکانی که این دامنه منتشرشان می‌کند. هم URLهای پزشک و هم صفحات تخصص از همین
// مجموعه ساخته می‌شوند تا نتوانند واگرا شوند.
async function getPublishedDoctors(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(
{
url: `${baseUrl}/doctor/${d.uuid}`,
changeFrequency: 'weekly',
priority: 0.8,
},
d.updated || d.created
)
);
.filter((d) => !scope.isRoot || !findDomainByCityId(extractEntityCityId(d)));
}
function getDoctorUrls(baseUrl, doctors) {
return doctors.map((d) =>
withLastModified(
{
url: `${baseUrl}/doctor/${d.uuid}`,
changeFrequency: 'weekly',
priority: 0.8,
},
d.updated || d.created
)
);
}
async function getClinicUrls(baseUrl, scope) {
@@ -151,14 +156,34 @@ async function getClinicUrls(baseUrl, scope) {
}
// صفحات فرود تخصص per-domain — روی هر دامنه «تخصص + آن شهر» است، پس همه‌جا self.
function getSpecialtyUrls(baseUrl) {
return specialtiesData
.filter((s) => s?.slug && s.status === 1)
.map((s) => ({
url: `${baseUrl}/specialties/${s.slug}`,
changeFrequency: 'weekly',
priority: 0.8,
}));
//
// فقط تخصص‌هایی که در همین دامنه واقعاً پزشک دارند اعلام می‌شوند؛ تولید بی‌قید هر ۹۳
// تخصص برای هر ۳۵ دامنه هزاران صفحهٔ خالی به گوگل معرفی می‌کرد (thin content انبوه).
//
// منبع، همان پزشکانی است که این sitemap منتشرشان می‌کند — نه endpoint شمارش. آن
// endpoint با فیلتر لیست پزشکان اختلاف دارد (مثلاً پزشکِ یافت‌شده از آدرس کلینیک را
// نمی‌شمارد)، و اگر sitemap از یک منبع و خودِ صفحه از منبع دیگر تصمیم بگیرد، صفحهٔ
// ایندکس‌پذیرِ اعلام‌نشده یا برعکس می‌سازیم. این‌طور هیچ فراخوانی اضافه‌ای هم ندارد.
function getSpecialtyUrls(baseUrl, doctors) {
const slugById = new Map(
specialtiesData
.filter((s) => s?.slug && s.status === 1)
.map((s) => [String(s.id), s.slug])
);
const slugs = new Set();
for (const doctor of doctors) {
for (const specialty of doctor?.specialties ?? []) {
const slug = slugById.get(String(specialty?.id));
if (slug) slugs.add(slug);
}
}
return [...slugs].map((slug) => ({
url: `${baseUrl}/specialties/${slug}`,
changeFrequency: 'weekly',
priority: 0.8,
}));
}
// هر پست فقط در sitemap دامنهٔ canonical خودش: پست شهریافته → دامنهٔ آن شهر،
@@ -193,15 +218,17 @@ export default async function sitemap() {
const baseUrl = getBaseUrl(domain);
const scope = getCityScope(domain);
const [doctorUrls, clinicUrls, blogUrls] = await Promise.all([
getDoctorUrls(baseUrl, scope),
const [doctors, clinicUrls, blogUrls] = await Promise.all([
getPublishedDoctors(scope),
getClinicUrls(baseUrl, scope),
getBlogUrls(baseUrl, scope),
]);
const doctorUrls = getDoctorUrls(baseUrl, doctors);
const allUrls = [
...getStaticPages(baseUrl),
...getSpecialtyUrls(baseUrl),
...getSpecialtyUrls(baseUrl, doctors),
...doctorUrls,
...clinicUrls,
...blogUrls,