import type { Database } from "@/lib/types"; import { listPleskDomains, listPleskSubscriptions } from "@/lib/plesk/client"; type SupabaseLike = any; type SyncResult = { instanceId: string; agencyId: string; skipped: boolean; reason?: string; subscriptionsUpserted: number; domainsUpserted: number; }; type InstanceRow = Pick< Database["public"]["Tables"]["plesk_instances"]["Row"], | "id" | "agency_id" | "base_url" | "auth_type" | "encrypted_secret" | "last_connected_at" >; type LocalSubscriptionLookupRow = { id: string; plesk_subscription_id: string; name: string | null; status: string | null; }; const MIN_INTERVAL_MS = 3 * 60 * 1000; function normalizeDomainLike(value: string) { return value.trim().toLowerCase().replace(/\.$/, ""); } export async function syncPleskInstance( supabase: SupabaseLike, instance: InstanceRow, actorUserId?: string | null, action = "sync", ): Promise { const startedAtIso = new Date().toISOString(); const logStatusSyncEvent = async ( eventAction: | "subscription_status_sync_started" | "subscription_status_sync_completed" | "subscription_status_sync_failed", metadata?: Record, status: "success" | "failed" = "success", errorMessage?: string | null, ) => { try { await supabase.from("actions_log").insert({ agency_id: instance.agency_id, actor_user_id: actorUserId ?? null, target_type: "plesk_instance", target_id: instance.id, action: eventAction, status, error_message: errorMessage ?? null, metadata: metadata ?? {}, }); } catch { // Logging should never block sync flow. } }; await supabase .from("plesk_instances") .update({ status: "syncing", last_sync_error: null, }) .eq("id", instance.id) .eq("agency_id", instance.agency_id); const last = instance.last_connected_at ? new Date(instance.last_connected_at).getTime() : 0; if (last && Date.now() - last < MIN_INTERVAL_MS) { await supabase .from("plesk_instances") .update({ status: "connected", last_sync_at: startedAtIso, last_sync_status: "skipped", last_sync_error: "recently_synced", }) .eq("id", instance.id) .eq("agency_id", instance.agency_id); return { instanceId: instance.id, agencyId: instance.agency_id, skipped: true, reason: "recently_synced", subscriptionsUpserted: 0, domainsUpserted: 0, }; } try { await logStatusSyncEvent("subscription_status_sync_started", { started_at: startedAtIso, }); const [subscriptionsRaw, domainsRaw] = await Promise.all([ listPleskSubscriptions({ baseUrl: instance.base_url, authType: instance.auth_type, encryptedSecret: instance.encrypted_secret, }), listPleskDomains({ baseUrl: instance.base_url, authType: instance.auth_type, encryptedSecret: instance.encrypted_secret, }), ]); const subscriptionsForUpsert = subscriptionsRaw .map((item: any) => { const sourceId = item?.plesk_subscription_id ?? item?.name ?? item?.id ?? item?.guid ?? item?.subscription_id; if (!sourceId) return null; return { agency_id: instance.agency_id, plesk_instance_id: instance.id, plesk_subscription_id: String(sourceId), name: item?.name ? String(item.name) : String(sourceId), status: item?.status ? String(item.status) : "unknown", status_raw: item?.status_raw ? String(item.status_raw) : null, last_status_sync_at: startedAtIso, owner_login: item?.owner_login ? String(item.owner_login) : item?.owner?.login ? String(item.owner.login) : null, plan_name: item?.plan_name ? String(item.plan_name) : item?.service_plan?.name ? String(item.service_plan.name) : null, }; }) .filter(Boolean) as Array>; const subscriptionStatusSyncFailures = subscriptionsRaw.filter( (item: any) => Boolean(item?.status_sync_error), ); if (subscriptionsForUpsert.length > 0) { const { error } = await supabase .from("plesk_subscriptions") .upsert(subscriptionsForUpsert, { onConflict: "agency_id,plesk_subscription_id", }); if (error) throw new Error(error.message); } const { data: localSubscriptions, error: localSubscriptionsError } = await supabase .from("plesk_subscriptions") .select("id, plesk_subscription_id, name, status") .eq("agency_id", instance.agency_id) .eq("plesk_instance_id", instance.id); if (localSubscriptionsError) { throw new Error(localSubscriptionsError.message); } const subscriptionsIndexed = ((localSubscriptions ?? []) as Array) .filter((row): row is LocalSubscriptionLookupRow => Boolean(row?.plesk_subscription_id && row?.id), ) .map((row) => ({ id: String(row.id), pleskSubscriptionId: String(row.plesk_subscription_id), status: row?.status ? String(row.status) : null, name: row?.name ? String(row.name) : null, })); const subscriptionByExternalId = new Map< string, { id: string; pleskSubscriptionId: string; status: string | null; name: string | null; } >(subscriptionsIndexed.map((row) => [row.pleskSubscriptionId, row])); const subscriptionByLocalId = new Map< string, { id: string; pleskSubscriptionId: string; status: string | null; name: string | null; } >(subscriptionsIndexed.map((row) => [row.id, row])); const subscriptionByName = new Map< string, { id: string; pleskSubscriptionId: string; status: string | null; name: string | null; } >( subscriptionsIndexed .filter((row) => row.name) .map((row) => [normalizeDomainLike(String(row.name)), row]), ); const domainsForUpsert = domainsRaw .map((item: any) => { const domainId = item?.id ?? item?.guid ?? item?.name; const domainName = item?.name ?? item?.domain_name ?? item?.domain; const normalizedDomainName = domainName ? normalizeDomainLike(String(domainName)) : null; const subExternalId = item?.subscription?.id ?? item?.subscription_id ?? item?.webspace_id; const rawExternalSubscriptionId = subExternalId ? String(subExternalId) : null; const subscriptionNameHint = item?.subscription?.name ?? item?.subscription_name ?? item?.webspace_name ?? item?.webspace ?? null; const localSubscriptionByExternal = rawExternalSubscriptionId ? subscriptionByExternalId.get(rawExternalSubscriptionId) : undefined; const localSubscriptionByLocalId = rawExternalSubscriptionId ? subscriptionByLocalId.get(rawExternalSubscriptionId) : undefined; const localSubscriptionByName = subscriptionNameHint ? subscriptionByName.get( normalizeDomainLike(String(subscriptionNameHint)), ) : undefined; const localSubscriptionByDomainName = normalizedDomainName ? subscriptionByName.get(normalizedDomainName) : undefined; const localSubscription = localSubscriptionByExternal ?? localSubscriptionByLocalId ?? localSubscriptionByName ?? localSubscriptionByDomainName; const externalSubscriptionId = localSubscription ? localSubscription.pleskSubscriptionId : subscriptionNameHint ? String(subscriptionNameHint) : rawExternalSubscriptionId; const rawCreated = item?.created_at ?? item?.created; const sourceCreatedAt = rawCreated ? new Date(String(rawCreated)).toISOString() : null; const aliasesCount = typeof item?.aliases_count === "number" ? item.aliases_count : Array.isArray(item?.aliases) ? item.aliases.length : null; if (!domainId || !domainName) return null; return { agency_id: instance.agency_id, plesk_instance_id: instance.id, subscription_id: localSubscription?.id ?? null, external_subscription_id: externalSubscriptionId, plesk_domain_id: String(domainId), domain_name: String(domainName), status: localSubscription?.status ?? "unknown", hosting_type: item?.hosting_type ? String(item.hosting_type) : null, source_created_at: sourceCreatedAt, aliases_count: aliasesCount, }; }) .filter(Boolean) as Array>; const unlinkedDomains = domainsForUpsert.filter( (domain) => !domain.subscription_id, ); if (domainsForUpsert.length > 0) { const { error } = await supabase .from("plesk_domains") .upsert(domainsForUpsert, { onConflict: "agency_id,plesk_domain_id", }); if (error) throw new Error(error.message); } await supabase .from("plesk_instances") .update({ last_connected_at: new Date().toISOString(), status: "connected", error_message: null, last_sync_at: startedAtIso, last_sync_status: "success", last_sync_error: null, last_sync_subscriptions: subscriptionsForUpsert.length, last_sync_domains: domainsForUpsert.length, }) .eq("id", instance.id) .eq("agency_id", instance.agency_id); await supabase.from("actions_log").insert({ agency_id: instance.agency_id, actor_user_id: actorUserId ?? null, target_type: "plesk_instance", target_id: instance.id, action, status: "success", metadata: { subscriptions_seen: subscriptionsRaw.length, domains_seen: domainsRaw.length, unlinked_domains: unlinkedDomains.length, unlinked_domain_samples: unlinkedDomains .slice(0, 25) .map((domain) => String(domain.domain_name ?? "unknown")), }, }); await logStatusSyncEvent("subscription_status_sync_completed", { synced_subscriptions: subscriptionsRaw.length, failed_subscriptions: subscriptionStatusSyncFailures.length, failed_subscription_ids: subscriptionStatusSyncFailures .slice(0, 50) .map((item: any) => String( item?.plesk_subscription_id ?? item?.subscription_id ?? "unknown", ), ), completed_at: new Date().toISOString(), }); if (subscriptionStatusSyncFailures.length > 0) { await logStatusSyncEvent( "subscription_status_sync_failed", { failed_subscriptions: subscriptionStatusSyncFailures.length, sample_errors: subscriptionStatusSyncFailures .slice(0, 20) .map((item: any) => ({ subscription: String( item?.plesk_subscription_id ?? item?.subscription_id ?? "unknown", ), error: String(item?.status_sync_error ?? "status_sync_failed"), })), }, "failed", `${subscriptionStatusSyncFailures.length} subscription status sync failures`, ); } return { instanceId: instance.id, agencyId: instance.agency_id, skipped: false, subscriptionsUpserted: subscriptionsForUpsert.length, domainsUpserted: domainsForUpsert.length, }; } catch (error) { const message = error instanceof Error ? error.message.slice(0, 1000) : "Sync failed"; await logStatusSyncEvent( "subscription_status_sync_failed", { failed_at: new Date().toISOString(), }, "failed", message, ); await supabase .from("plesk_instances") .update({ status: "error", error_message: message, last_sync_at: startedAtIso, last_sync_status: "failed", last_sync_error: message, }) .eq("id", instance.id) .eq("agency_id", instance.agency_id); throw error; } } export async function backfillDomainSubscriptionLinks( supabase: SupabaseLike, input: { agencyId: string; instanceId: string; actorUserId?: string | null; }, ) { const { agencyId, instanceId, actorUserId } = input; const { data: subscriptions, error: subscriptionsError } = await supabase .from("plesk_subscriptions") .select("id, plesk_subscription_id, name, status") .eq("agency_id", agencyId) .eq("plesk_instance_id", instanceId); if (subscriptionsError) { throw new Error(subscriptionsError.message); } const { data: domains, error: domainsError } = await supabase .from("plesk_domains") .select("id, domain_name, external_subscription_id, subscription_id") .eq("agency_id", agencyId) .eq("plesk_instance_id", instanceId) .is("subscription_id", null); if (domainsError) { throw new Error(domainsError.message); } const subscriptionByExternalId = new Map< string, { id: string; status: string } >( (subscriptions ?? []) .filter((subscription: any) => subscription?.plesk_subscription_id) .map((subscription: any) => [ String(subscription.plesk_subscription_id), { id: String(subscription.id), status: subscription?.status ? String(subscription.status) : "unknown", }, ]), ); const subscriptionByName = new Map( (subscriptions ?? []) .filter((subscription: any) => subscription?.name) .map((subscription: any) => [ normalizeDomainLike(String(subscription.name)), { id: String(subscription.id), status: subscription?.status ? String(subscription.status) : "unknown", }, ]), ); let linkedCount = 0; const failures: Array<{ domain_id: string; error: string }> = []; for (const domain of domains ?? []) { const matchByExternal = domain?.external_subscription_id ? subscriptionByExternalId.get(String(domain.external_subscription_id)) : undefined; const matchByDomainName = domain?.domain_name ? subscriptionByName.get(normalizeDomainLike(String(domain.domain_name))) : undefined; const linkedSubscription = matchByExternal ?? matchByDomainName; if (!linkedSubscription) continue; const { error: updateError } = await supabase .from("plesk_domains") .update({ subscription_id: linkedSubscription.id, status: linkedSubscription.status, }) .eq("id", domain.id) .eq("agency_id", agencyId); if (updateError) { failures.push({ domain_id: String(domain.id), error: updateError.message, }); continue; } linkedCount += 1; } await supabase.from("actions_log").insert({ agency_id: agencyId, actor_user_id: actorUserId ?? null, target_type: "plesk_instance", target_id: instanceId, action: "domain_subscription_backfill", status: failures.length > 0 ? "failed" : "success", error_message: failures.length > 0 ? `${failures.length} domain links failed during backfill` : null, metadata: { attempted: (domains ?? []).length, linked: linkedCount, failures: failures.slice(0, 25), }, }); return { attempted: (domains ?? []).length, linked: linkedCount, failed: failures.length, }; }