import { NextResponse } from "next/server"; import { z } from "zod"; import { assertAgencyAdmin, getRouteAgencyContextOrThrow } from "@/lib/agency"; import { assertRateLimit } from "@/lib/api/rate-limit"; import { assertSameOrigin } from "@/lib/api/security"; import { parseSubscriptionStatusDetailsFromInfo, pleskCliCall, } from "@/lib/plesk/client"; import { createSupabaseRouteClient } from "@/lib/supabase/route"; const actionSchema = z.object({ action: z.enum(["suspend", "unsuspend"]), subscription_id: z.string().uuid().optional(), }); const LOG_EXCERPT_LIMIT = 500; const DOMAIN_ACTION_EVENTS = { suspend: { requested: "domain_suspend_requested", succeeded: "domain_suspend_succeeded", failed: "domain_suspend_failed", }, unsuspend: { requested: "domain_unsuspend_requested", succeeded: "domain_unsuspend_succeeded", failed: "domain_unsuspend_failed", }, } as const; function excerpt(value: string | null | undefined) { if (!value) return null; const trimmed = value.trim(); if (!trimmed) return null; return trimmed.length > LOG_EXCERPT_LIMIT ? `${trimmed.slice(0, LOG_EXCERPT_LIMIT)}…` : trimmed; } async function insertActionLogBestEffort( supabase: any, payload: Record, ) { try { const { error } = await supabase.from("actions_log").insert(payload); if (error) { console.error("[actions_log] insert failed", error.message); } } catch (error) { console.error("[actions_log] insert threw", error); } } export async function POST( req: Request, { params }: { params: { id: string } }, ) { const supabase = createSupabaseRouteClient() as any; let context: Awaited> | null = null; let domain: { id: string; subscription_id: string | null } | null = null; let subscription: { id: string; plesk_subscription_id: string; plesk_instance_id: string; } | null = null; let requestedAction: "suspend" | "unsuspend" | null = null; const startedAt = Date.now(); try { assertSameOrigin(req); context = await getRouteAgencyContextOrThrow(); assertAgencyAdmin(context); assertRateLimit(`plesk-domain-action:${context.userId}`, 10, 60_000); const payload = actionSchema.parse(await req.json()); const { action } = payload; requestedAction = action; const { data: domainRow, error: domainError } = await supabase .from("plesk_domains") .select("id, agency_id, plesk_instance_id, subscription_id") .eq("id", params.id) .eq("agency_id", context.agencyId) .single(); if (domainError || !domainRow) { throw new Error("Domain not found"); } const resolvedSubscriptionId = payload.subscription_id ?? (domainRow.subscription_id ? String(domainRow.subscription_id) : null); domain = { id: String(domainRow.id), subscription_id: resolvedSubscriptionId, }; if (!resolvedSubscriptionId) { throw new Error("Domain is not linked to a subscription"); } const { data: subscriptionRow, error: subscriptionError } = await supabase .from("plesk_subscriptions") .select("id, plesk_subscription_id, plesk_instance_id") .eq("id", resolvedSubscriptionId) .eq("agency_id", context.agencyId) .single(); if (subscriptionError || !subscriptionRow) { throw new Error("Subscription not found"); } subscription = { id: String(subscriptionRow.id), plesk_subscription_id: String(subscriptionRow.plesk_subscription_id), plesk_instance_id: String(subscriptionRow.plesk_instance_id), }; const { data: instance, error: instanceError } = await supabase .from("plesk_instances") .select("id, base_url, auth_type, encrypted_secret") .eq("id", subscriptionRow.plesk_instance_id) .eq("agency_id", context.agencyId) .single(); if (instanceError || !instance) { throw new Error("Plesk instance not found"); } const connection = { baseUrl: instance.base_url, authType: instance.auth_type, encryptedSecret: instance.encrypted_secret, } as const; const domainIdentifier = subscription.plesk_subscription_id; const siteArgs = action === "suspend" ? ["--suspend", domainIdentifier] : ["--on", domainIdentifier]; await insertActionLogBestEffort(supabase, { agency_id: context.agencyId, actor_user_id: context.userId, target_type: "plesk_domain", target_id: domain.id, action: DOMAIN_ACTION_EVENTS[action].requested, status: "pending", metadata: { domain_id: domain.id, subscription_id: subscription.id, plesk_instance_id: subscription.plesk_instance_id, plesk_subscription_id: subscription.plesk_subscription_id, cli_command: "site", cli_args: siteArgs, }, }); const siteResult = await pleskCliCall(connection, "site", siteArgs); const fallbackStatus = action === "suspend" ? "suspended" : "active"; let refreshedStatus = fallbackStatus; let refreshedStatusRaw: string | null = null; let refreshErrorMessage: string | null = null; try { const infoResult = await pleskCliCall(connection, "subscription", [ "--info", domainIdentifier, ]); const parsed = parseSubscriptionStatusDetailsFromInfo( infoResult.stdout ?? "", ); refreshedStatus = parsed.status; refreshedStatusRaw = parsed.statusRaw; if ( action === "unsuspend" && refreshedStatus === "suspended" && (refreshedStatusRaw ?? "").toLowerCase().includes("subscriber") ) { throw new Error( "Domain remains suspended because its subscriber/customer account is suspended.", ); } } catch (refreshError) { refreshErrorMessage = refreshError instanceof Error ? refreshError.message.slice(0, 1000) : "subscription_status_refresh_failed"; await insertActionLogBestEffort(supabase, { agency_id: context.agencyId, actor_user_id: context.userId, target_type: "plesk_subscription", target_id: subscription.id, action: "subscription_status_refresh_failed", status: "failed", error_message: refreshErrorMessage, metadata: { subscription_id: subscription.id, plesk_subscription_id: subscription.plesk_subscription_id, plesk_instance_id: subscription.plesk_instance_id, source: "domain_action", domain_id: domain.id, }, }); } await supabase .from("plesk_subscriptions") .update({ status: refreshedStatus, status_raw: refreshedStatusRaw, last_status_sync_at: new Date().toISOString(), }) .eq("id", subscription.id) .eq("agency_id", context.agencyId); await supabase .from("plesk_domains") .update({ status: refreshedStatus }) .eq("agency_id", context.agencyId) .eq("subscription_id", subscription.id); await insertActionLogBestEffort(supabase, { agency_id: context.agencyId, actor_user_id: context.userId, target_type: "plesk_domain", target_id: domain.id, action: DOMAIN_ACTION_EVENTS[action].succeeded, status: "success", metadata: { duration_ms: Date.now() - startedAt, domain_id: domain.id, subscription_id: subscription.id, plesk_instance_id: subscription.plesk_instance_id, plesk_subscription_id: subscription.plesk_subscription_id, cli_command: "site", cli_args: siteArgs, cli_exit_code: siteResult.code, cli_stdout_excerpt: excerpt(siteResult.stdout), cli_stderr_excerpt: excerpt(siteResult.stderr), refreshed_status: refreshedStatus, refresh_error: refreshErrorMessage, }, }); return NextResponse.json({ ok: true }); } catch (error) { if (context && domain) { const message = error instanceof Error ? error.message : "Domain action failed"; if (requestedAction) { await insertActionLogBestEffort(supabase, { agency_id: context.agencyId, actor_user_id: context.userId, target_type: "plesk_domain", target_id: domain.id, action: DOMAIN_ACTION_EVENTS[requestedAction].failed, status: "failed", error_message: message, metadata: { duration_ms: Date.now() - startedAt, domain_id: domain.id, subscription_id: subscription?.id ?? domain.subscription_id, plesk_instance_id: subscription?.plesk_instance_id ?? null, plesk_subscription_id: subscription?.plesk_subscription_id ?? null, }, }); } else { await insertActionLogBestEffort(supabase, { agency_id: context.agencyId, actor_user_id: context.userId, target_type: "plesk_domain", target_id: domain.id, action: "domain_action_failed", status: "failed", error_message: message, metadata: { duration_ms: Date.now() - startedAt, domain_id: domain.id, subscription_id: subscription?.id ?? domain.subscription_id, plesk_instance_id: subscription?.plesk_instance_id ?? null, plesk_subscription_id: subscription?.plesk_subscription_id ?? null, }, }); } } return NextResponse.json( { error: error instanceof Error ? error.message : "Domain action failed", }, { status: 400 }, ); } }