diff --git a/app/api/plesk/domains/[id]/action/route.ts b/app/api/plesk/domains/[id]/action/route.ts index 621c8bf..0dda023 100644 --- a/app/api/plesk/domains/[id]/action/route.ts +++ b/app/api/plesk/domains/[id]/action/route.ts @@ -15,6 +15,44 @@ const actionSchema = z.object({ 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 } }, @@ -29,6 +67,7 @@ export async function POST( plesk_instance_id: string; } | null = null; let requestedAction: "suspend" | "unsuspend" | null = null; + const startedAt = Date.now(); try { assertSameOrigin(req); @@ -105,22 +144,25 @@ export async function POST( ? ["--suspend", domainIdentifier] : ["--on", domainIdentifier]; - console.info("[domain_action] site CLI", { - action, - command: "site", - args: siteArgs, - domain_id: domain.id, - 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].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); - console.info("[domain_action] site CLI result", { - code: siteResult.code, - stdout: siteResult.stdout, - stderr: siteResult.stderr, - }); - const fallbackStatus = action === "suspend" ? "suspended" : "active"; let refreshedStatus = fallbackStatus; let refreshedStatusRaw: string | null = null; @@ -132,12 +174,6 @@ export async function POST( domainIdentifier, ]); - console.info("[domain_action] subscription info result", { - code: infoResult.code, - stdout: infoResult.stdout, - stderr: infoResult.stderr, - }); - const parsed = parseSubscriptionStatusDetailsFromInfo( infoResult.stdout ?? "", ); @@ -159,7 +195,7 @@ export async function POST( ? refreshError.message.slice(0, 1000) : "subscription_status_refresh_failed"; - await supabase.from("actions_log").insert({ + await insertActionLogBestEffort(supabase, { agency_id: context.agencyId, actor_user_id: context.userId, target_type: "plesk_subscription", @@ -193,17 +229,24 @@ export async function POST( .eq("agency_id", context.agencyId) .eq("subscription_id", subscription.id); - await supabase.from("actions_log").insert({ + await insertActionLogBestEffort(supabase, { agency_id: context.agencyId, actor_user_id: context.userId, target_type: "plesk_domain", target_id: domain.id, - action: `domain_${action}`, + 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, }, @@ -214,20 +257,41 @@ export async function POST( if (context && domain) { const message = error instanceof Error ? error.message : "Domain action failed"; - await supabase.from("actions_log").insert({ - agency_id: context.agencyId, - actor_user_id: context.userId, - target_type: "plesk_domain", - target_id: domain.id, - action: requestedAction ? `domain_${requestedAction}` : "domain_action", - status: "failed", - error_message: message, - metadata: { - domain_id: domain.id, - subscription_id: subscription?.id ?? domain.subscription_id, - plesk_subscription_id: subscription?.plesk_subscription_id ?? null, - }, - }); + 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( diff --git a/app/api/plesk/subscriptions/[id]/action/route.ts b/app/api/plesk/subscriptions/[id]/action/route.ts index be43686..91a6609 100644 --- a/app/api/plesk/subscriptions/[id]/action/route.ts +++ b/app/api/plesk/subscriptions/[id]/action/route.ts @@ -16,6 +16,44 @@ const actionSchema = z.object({ action: z.enum(["suspend", "unsuspend"]), }); +const LOG_EXCERPT_LIMIT = 500; + +const SUBSCRIPTION_ACTION_EVENTS = { + suspend: { + requested: "subscription_suspend_requested", + succeeded: "subscription_suspend_succeeded", + failed: "subscription_suspend_failed", + }, + unsuspend: { + requested: "subscription_unsuspend_requested", + succeeded: "subscription_unsuspend_succeeded", + failed: "subscription_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 } }, @@ -29,6 +67,7 @@ export async function POST( plesk_subscription_id: string; plesk_instance_id: string; } | null = null; + const startedAt = Date.now(); try { assertSameOrigin(req); @@ -70,17 +109,37 @@ export async function POST( encryptedSecret: instance.encrypted_secret, } as const; - if (payload.action === "suspend") { - await suspendPleskSubscription( - connection, - subscription.plesk_subscription_id, - ); - } else { - await unsuspendPleskSubscription( - connection, - subscription.plesk_subscription_id, - ); - } + const cliArgs = + payload.action === "suspend" + ? ["--webspace-off", subscription.plesk_subscription_id] + : ["--unsuspend", subscription.plesk_subscription_id]; + + await insertActionLogBestEffort(supabase, { + agency_id: context.agencyId, + actor_user_id: context.userId, + target_type: "plesk_subscription", + target_id: subscription.id, + action: SUBSCRIPTION_ACTION_EVENTS[payload.action].requested, + status: "pending", + metadata: { + subscription_id: subscription.id, + plesk_instance_id: subscription.plesk_instance_id, + plesk_subscription_id: subscription.plesk_subscription_id, + cli_command: "subscription", + cli_args: cliArgs, + }, + }); + + const actionCliResult = + payload.action === "suspend" + ? await suspendPleskSubscription( + connection, + subscription.plesk_subscription_id, + ) + : await unsuspendPleskSubscription( + connection, + subscription.plesk_subscription_id, + ); const fallbackStatus = payload.action === "suspend" ? "suspended" : "active"; @@ -104,7 +163,7 @@ export async function POST( ? refreshError.message.slice(0, 1000) : "subscription_status_refresh_failed"; - await supabase.from("actions_log").insert({ + await insertActionLogBestEffort(supabase, { agency_id: context.agencyId, actor_user_id: context.userId, target_type: "plesk_subscription", @@ -135,17 +194,23 @@ export async function POST( .eq("agency_id", context.agencyId) .eq("subscription_id", subscription.id); - await supabase.from("actions_log").insert({ + await insertActionLogBestEffort(supabase, { agency_id: context.agencyId, actor_user_id: context.userId, target_type: "plesk_subscription", target_id: subscription.id, - action: payload.action, + action: SUBSCRIPTION_ACTION_EVENTS[payload.action].succeeded, status: "success", metadata: { + duration_ms: Date.now() - startedAt, + cli_command: "subscription", + cli_args: cliArgs, plesk_subscription_id: subscription.plesk_subscription_id, refreshed_status: refreshedStatus, refresh_error: refreshErrorMessage, + cli_exit_code: actionCliResult.code, + cli_stdout_excerpt: excerpt(actionCliResult.stdout), + cli_stderr_excerpt: excerpt(actionCliResult.stderr), db_status_update_error: updateError?.message ?? null, }, }); @@ -160,18 +225,37 @@ export async function POST( if (supabase && context && subscription) { const message = error instanceof Error ? error.message : "Subscription action failed"; - await supabase.from("actions_log").insert({ - agency_id: context.agencyId, - actor_user_id: context.userId, - target_type: "plesk_subscription", - target_id: subscription.id, - action: requestedAction ?? "subscription_action", - status: "failed", - error_message: message, - metadata: { - plesk_subscription_id: subscription.plesk_subscription_id, - }, - }); + if (requestedAction) { + await insertActionLogBestEffort(supabase, { + agency_id: context.agencyId, + actor_user_id: context.userId, + target_type: "plesk_subscription", + target_id: subscription.id, + action: SUBSCRIPTION_ACTION_EVENTS[requestedAction].failed, + status: "failed", + error_message: message, + metadata: { + duration_ms: Date.now() - startedAt, + plesk_subscription_id: subscription.plesk_subscription_id, + plesk_instance_id: subscription.plesk_instance_id, + }, + }); + } else { + await insertActionLogBestEffort(supabase, { + agency_id: context.agencyId, + actor_user_id: context.userId, + target_type: "plesk_subscription", + target_id: subscription.id, + action: "subscription_action_failed", + status: "failed", + error_message: message, + metadata: { + duration_ms: Date.now() - startedAt, + plesk_subscription_id: subscription.plesk_subscription_id, + plesk_instance_id: subscription.plesk_instance_id, + }, + }); + } } return NextResponse.json(