159 lines
4.9 KiB
TypeScript
159 lines
4.9 KiB
TypeScript
import { getOrderUpdateForDeliveryInvitationAction } from "../_shared/delivery-invitations.ts";
|
||
import { requireUuid } from "../_shared/security.ts";
|
||
import { createServiceClient } from "../_shared/chatbot.ts";
|
||
import { insertIntegrationEvent } from "../_shared/integration-events.ts";
|
||
import {
|
||
getCorsHeaders,
|
||
jsonResponse,
|
||
readJsonBody,
|
||
requireRateLimit,
|
||
verifyInternalRequest,
|
||
} from "../_shared/security.ts";
|
||
|
||
const MAX_BODY_BYTES = 16 * 1024;
|
||
|
||
type ReportBody = {
|
||
orderId?: string;
|
||
result?: "delivered" | "problem";
|
||
note?: string;
|
||
payload?: Record<string, unknown>;
|
||
};
|
||
|
||
Deno.serve(async (request) => {
|
||
if (request.method === "OPTIONS") {
|
||
const corsHeaders = getCorsHeaders(request, "integration");
|
||
return corsHeaders ? new Response("ok", { headers: corsHeaders }) : jsonResponse({ error: "Origin not allowed" }, 403);
|
||
}
|
||
|
||
if (request.method !== "POST") {
|
||
return jsonResponse({ error: "Method not allowed" }, 405);
|
||
}
|
||
|
||
const corsHeaders = getCorsHeaders(request, "integration") || {};
|
||
|
||
try {
|
||
const { body, rawBody } = await readJsonBody<ReportBody>(request, {
|
||
maxBytes: MAX_BODY_BYTES,
|
||
});
|
||
await verifyInternalRequest(request, rawBody, { rawBody });
|
||
|
||
if (!body.orderId) {
|
||
return jsonResponse({ error: "orderId is required" }, 400, corsHeaders);
|
||
}
|
||
|
||
try {
|
||
requireUuid(body.orderId, "orderId");
|
||
} catch (e) {
|
||
return jsonResponse({ ok: false, error: (e as Error).message }, 400, corsHeaders);
|
||
}
|
||
|
||
const supabase = createServiceClient();
|
||
await requireRateLimit(supabase, {
|
||
scope: "delivery-report",
|
||
key: body.orderId,
|
||
maxCount: 10,
|
||
windowSeconds: 600,
|
||
blockSeconds: 1800,
|
||
});
|
||
|
||
const { data: currentOrder, error: orderError } = await supabase
|
||
.from("orders")
|
||
.select("id, status, delivery_agreement_status")
|
||
.eq("id", body.orderId)
|
||
.single();
|
||
|
||
if (orderError) {
|
||
throw orderError;
|
||
}
|
||
|
||
const isDelivered = body.result === "delivered";
|
||
const action = isDelivered ? "mark_delivered" : "mark_paid_storage";
|
||
const orderUpdate = getOrderUpdateForDeliveryInvitationAction(action);
|
||
const nextStatus = isDelivered ? orderUpdate?.status || "Доставлен" : "Проблема доставки";
|
||
|
||
const { error: invitationError } = await supabase
|
||
.from("delivery_invitations")
|
||
.update({
|
||
state: isDelivered ? "delivered" : "paid_storage",
|
||
...(isDelivered ? { delivered_at: new Date().toISOString() } : { paid_storage_at: new Date().toISOString() }),
|
||
})
|
||
.eq("order_id", body.orderId);
|
||
|
||
if (invitationError) {
|
||
throw invitationError;
|
||
}
|
||
|
||
const { error: updateError } = await supabase
|
||
.from("orders")
|
||
.update({
|
||
status: nextStatus,
|
||
delivery_agreement_status: isDelivered
|
||
? "Подтверждено клиентом"
|
||
: body.note || currentOrder.delivery_agreement_status || "Ошибка отправки",
|
||
})
|
||
.eq("id", body.orderId);
|
||
|
||
if (updateError) {
|
||
throw updateError;
|
||
}
|
||
|
||
const { error: historyError } = await supabase.from("order_history").insert({
|
||
order_id: body.orderId,
|
||
action: isDelivered ? "Подтверждение доставки" : "Фиксация проблемы доставки",
|
||
old_status: currentOrder.status,
|
||
new_status: isDelivered ? "Доставлен" : "Проблема доставки",
|
||
metadata: {
|
||
old_delivery_agreement_status: currentOrder.delivery_agreement_status,
|
||
new_delivery_agreement_status: isDelivered
|
||
? "Подтверждено клиентом"
|
||
: body.note || currentOrder.delivery_agreement_status || "Ошибка отправки",
|
||
payload: body.payload || {},
|
||
},
|
||
});
|
||
|
||
if (historyError) {
|
||
throw historyError;
|
||
}
|
||
|
||
await insertIntegrationEvent(supabase, {
|
||
order_id: body.orderId,
|
||
event_type: isDelivered ? "delivery_result_delivered" : "delivery_result_problem",
|
||
direction: "internal",
|
||
status: "success",
|
||
payload: {
|
||
result: body.result || null,
|
||
note: body.note || null,
|
||
payload: body.payload || {},
|
||
},
|
||
});
|
||
|
||
return jsonResponse(
|
||
{
|
||
ok: true,
|
||
orderId: body.orderId,
|
||
status: nextStatus,
|
||
deliveryAgreementStatus: isDelivered
|
||
? "Подтверждено клиентом"
|
||
: body.note || currentOrder.delivery_agreement_status || "Ошибка отправки",
|
||
workflowStatus: nextStatus,
|
||
},
|
||
200,
|
||
corsHeaders,
|
||
);
|
||
} catch (error) {
|
||
if (error instanceof Error && "status" in error) {
|
||
const httpError = error as { status: number; message: string };
|
||
return jsonResponse({ ok: false, error: httpError.message }, httpError.status, corsHeaders);
|
||
}
|
||
|
||
return jsonResponse(
|
||
{
|
||
ok: false,
|
||
error: error instanceof Error ? error.message : "Unexpected error",
|
||
},
|
||
500,
|
||
corsHeaders,
|
||
);
|
||
}
|
||
});
|