diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index 360c423..8b73c5c 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -21,6 +21,22 @@ function normalizeDomainLike(value: string) { return value.trim().toLowerCase().replace(/\.$/, ""); } +function findBestSuffixStatusMatch( + normalizedDomain: string, + candidates: Array<{ normalizedName: string; status: string | null }>, +) { + let best: { normalizedName: string; status: string | null } | null = null; + + for (const candidate of candidates) { + if (!normalizedDomain.endsWith(`.${candidate.normalizedName}`)) continue; + if (!best || candidate.normalizedName.length > best.normalizedName.length) { + best = candidate; + } + } + + return best?.status ?? null; +} + export default async function DashboardPage() { const context = await getServerAgencyContextOrRedirect(); const supabase = createSupabaseServerClient() as any; @@ -163,6 +179,24 @@ export default async function DashboardPage() { ]), ); + const subscriptionStatusNameCandidatesByInstance = new Map< + string, + Array<{ normalizedName: string; status: string | null }> + >(); + + for (const subscription of domainSubscriptions ?? []) { + if (!subscription?.plesk_instance_id || !subscription?.name) continue; + + const instanceId = String(subscription.plesk_instance_id); + const existing = + subscriptionStatusNameCandidatesByInstance.get(instanceId) ?? []; + existing.push({ + normalizedName: normalizeDomainLike(String(subscription.name)), + status: subscription?.status ? String(subscription.status) : null, + }); + subscriptionStatusNameCandidatesByInstance.set(instanceId, existing); + } + const autoSyncByInstance = new Map< string, { status: string; created_at: string; error_message: string | null } @@ -229,10 +263,17 @@ export default async function DashboardPage() { const joinedStatusByName = subscriptionStatusByInstanceAndName.get( `${domain.plesk_instance_id}:${normalizeDomainLike(domain.domain_name)}`, ); + const joinedStatusBySuffix = findBestSuffixStatusMatch( + normalizeDomainLike(domain.domain_name), + subscriptionStatusNameCandidatesByInstance.get( + domain.plesk_instance_id, + ) ?? [], + ); const joinedStatus = joinedStatusByLocalId ?? joinedStatusByExternalId ?? - joinedStatusByName; + joinedStatusByName ?? + joinedStatusBySuffix; return { ...domain, diff --git a/lib/plesk/sync.ts b/lib/plesk/sync.ts index 7ac1355..10d4468 100644 --- a/lib/plesk/sync.ts +++ b/lib/plesk/sync.ts @@ -35,6 +35,21 @@ function normalizeDomainLike(value: string) { return value.trim().toLowerCase().replace(/\.$/, ""); } +function findBestSuffixSubscriptionMatch< + T extends { normalizedName: string; id: string; status: string | null }, +>(normalizedDomain: string, candidates: T[]) { + let best: T | undefined; + + for (const candidate of candidates) { + if (!normalizedDomain.endsWith(`.${candidate.normalizedName}`)) continue; + if (!best || candidate.normalizedName.length > best.normalizedName.length) { + best = candidate; + } + } + + return best; +} + export async function syncPleskInstance( supabase: SupabaseLike, instance: InstanceRow, @@ -220,6 +235,20 @@ export async function syncPleskInstance( .map((row) => [normalizeDomainLike(String(row.name)), row]), ); + const subscriptionNameCandidates: Array<{ + id: string; + pleskSubscriptionId: string; + status: string | null; + normalizedName: string; + }> = subscriptionsIndexed + .filter((row) => row.name) + .map((row) => ({ + id: row.id, + pleskSubscriptionId: row.pleskSubscriptionId, + status: row.status, + normalizedName: normalizeDomainLike(String(row.name)), + })); + const domainsForUpsert = domainsRaw .map((item: any) => { const domainId = item?.id ?? item?.guid ?? item?.name; @@ -253,11 +282,18 @@ export async function syncPleskInstance( const localSubscriptionByDomainName = normalizedDomainName ? subscriptionByName.get(normalizedDomainName) : undefined; + const localSubscriptionBySuffix = normalizedDomainName + ? findBestSuffixSubscriptionMatch( + normalizedDomainName, + subscriptionNameCandidates, + ) + : undefined; const localSubscription = localSubscriptionByExternal ?? localSubscriptionByLocalId ?? localSubscriptionByName ?? - localSubscriptionByDomainName; + localSubscriptionByDomainName ?? + localSubscriptionBySuffix; const externalSubscriptionId = localSubscription ? localSubscription.pleskSubscriptionId @@ -469,6 +505,18 @@ export async function backfillDomainSubscriptionLinks( ]), ); + const subscriptionNameCandidates: Array<{ + id: string; + status: string; + normalizedName: string; + }> = (subscriptions ?? []) + .filter((subscription: any) => subscription?.name) + .map((subscription: any) => ({ + id: String(subscription.id), + status: subscription?.status ? String(subscription.status) : "unknown", + normalizedName: normalizeDomainLike(String(subscription.name)), + })); + let linkedCount = 0; const failures: Array<{ domain_id: string; error: string }> = []; @@ -479,7 +527,17 @@ export async function backfillDomainSubscriptionLinks( const matchByDomainName = domain?.domain_name ? subscriptionByName.get(normalizeDomainLike(String(domain.domain_name))) : undefined; - const linkedSubscription = matchByExternal ?? matchByDomainName; + const normalizedDomainName = domain?.domain_name + ? normalizeDomainLike(String(domain.domain_name)) + : null; + const matchBySuffix = normalizedDomainName + ? findBestSuffixSubscriptionMatch( + normalizedDomainName, + subscriptionNameCandidates, + ) + : undefined; + const linkedSubscription = + matchByExternal ?? matchByDomainName ?? matchBySuffix; if (!linkedSubscription) continue;