Compare commits

..

4 Commits

2 changed files with 106 additions and 8 deletions

View File

@@ -5,6 +5,8 @@ import { assertAgencyAdmin, getRouteAgencyContextOrThrow } from "@/lib/agency";
import { assertRateLimit } from "@/lib/api/rate-limit";
import { assertSameOrigin } from "@/lib/api/security";
import {
parseSubscriptionStatusDetailsFromInfo,
pleskCliCall,
suspendPleskSubscription,
unsuspendPleskSubscription,
} from "@/lib/plesk/client";
@@ -80,12 +82,59 @@ export async function POST(
);
}
const newStatus = payload.action === "suspend" ? "suspended" : "active";
const fallbackStatus =
payload.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",
subscription.plesk_subscription_id,
]);
const parsed = parseSubscriptionStatusDetailsFromInfo(
infoResult.stdout ?? "",
);
refreshedStatus = parsed.status;
refreshedStatusRaw = parsed.statusRaw;
} catch (refreshError) {
refreshErrorMessage =
refreshError instanceof Error
? refreshError.message.slice(0, 1000)
: "subscription_status_refresh_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: "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,
},
});
}
const { error: updateError } = await supabase
.from("plesk_subscriptions")
.update({ status: newStatus })
.update({
status: refreshedStatus,
status_raw: refreshedStatusRaw,
last_status_sync_at: new Date().toISOString(),
})
.eq("id", subscription.id);
await supabase
.from("plesk_domains")
.update({ status: refreshedStatus })
.eq("agency_id", context.agencyId)
.eq("subscription_id", subscription.id);
await supabase.from("actions_log").insert({
agency_id: context.agencyId,
actor_user_id: context.userId,
@@ -95,14 +144,17 @@ export async function POST(
status: "success",
metadata: {
plesk_subscription_id: subscription.plesk_subscription_id,
refreshed_status: refreshedStatus,
refresh_error: refreshErrorMessage,
db_status_update_error: updateError?.message ?? null,
},
});
return NextResponse.json({
ok: true,
status: newStatus,
status: refreshedStatus,
dbStatusUpdated: !updateError,
refreshError: refreshErrorMessage,
});
} catch (error) {
if (supabase && context && subscription) {

View File

@@ -17,6 +17,10 @@ type ActionLogSummary = Pick<
"target_id" | "status" | "action" | "created_at" | "error_message"
>;
function normalizeDomainLike(value: string) {
return value.trim().toLowerCase().replace(/\.$/, "");
}
export default async function DashboardPage() {
const context = await getServerAgencyContextOrRedirect();
const supabase = createSupabaseServerClient() as any;
@@ -50,7 +54,7 @@ export default async function DashboardPage() {
supabase
.from("plesk_domains")
.select(
"id, plesk_instance_id, subscription_id, status, domain_name, hosting_type, source_created_at, aliases_count, updated_at",
"id, plesk_instance_id, subscription_id, external_subscription_id, status, domain_name, hosting_type, source_created_at, aliases_count, updated_at",
)
.eq("agency_id", context.agencyId)
.order("updated_at", { ascending: false })
@@ -101,6 +105,7 @@ export default async function DashboardPage() {
id: string;
plesk_instance_id: string;
subscription_id: string | null;
external_subscription_id: string | null;
domain_name: string;
status: string | null;
hosting_type: string | null;
@@ -121,13 +126,17 @@ export default async function DashboardPage() {
),
);
const domainInstanceIds = Array.from(
new Set((domains ?? []).map((domain) => domain.plesk_instance_id)),
);
const { data: domainSubscriptions } =
domainSubscriptionIds.length > 0
domainInstanceIds.length > 0
? await supabase
.from("plesk_subscriptions")
.select("id, status")
.select("id, plesk_instance_id, plesk_subscription_id, name, status")
.eq("agency_id", context.agencyId)
.in("id", domainSubscriptionIds)
.in("plesk_instance_id", domainInstanceIds)
: { data: [] };
const subscriptionStatusById = new Map<string, string | null>(
@@ -137,6 +146,31 @@ export default async function DashboardPage() {
]),
);
const subscriptionStatusByExternalId = new Map<string, string | null>(
(domainSubscriptions ?? [])
.filter((subscription: any) =>
Boolean(
subscription?.plesk_instance_id &&
subscription?.plesk_subscription_id,
),
)
.map((subscription: any) => [
`${String(subscription.plesk_instance_id)}:${String(subscription.plesk_subscription_id)}`,
subscription?.status ? String(subscription.status) : null,
]),
);
const subscriptionStatusByInstanceAndName = new Map<string, string | null>(
(domainSubscriptions ?? [])
.filter((subscription: any) =>
Boolean(subscription?.plesk_instance_id && subscription?.name),
)
.map((subscription: any) => [
`${String(subscription.plesk_instance_id)}:${normalizeDomainLike(String(subscription.name))}`,
subscription?.status ? String(subscription.status) : null,
]),
);
const autoSyncByInstance = new Map<
string,
{ status: string; created_at: string; error_message: string | null }
@@ -192,9 +226,21 @@ export default async function DashboardPage() {
instances={instances ?? []}
subscriptions={subscriptions ?? []}
domains={(domains ?? []).map((domain) => {
const joinedStatus = domain.subscription_id
const joinedStatusByLocalId = domain.subscription_id
? subscriptionStatusById.get(domain.subscription_id)
: null;
const joinedStatusByExternalId = domain.external_subscription_id
? subscriptionStatusByExternalId.get(
`${domain.plesk_instance_id}:${domain.external_subscription_id}`,
)
: null;
const joinedStatusByName = subscriptionStatusByInstanceAndName.get(
`${domain.plesk_instance_id}:${normalizeDomainLike(domain.domain_name)}`,
);
const joinedStatus =
joinedStatusByLocalId ??
joinedStatusByExternalId ??
joinedStatusByName;
return {
...domain,