dreame-shopify-app/app/routes/api.action.activity.check.tsx

105 lines
3.5 KiB
TypeScript
Raw Normal View History

2026-06-19 00:53:13 +08:00
import { json, type ActionFunctionArgs, type LoaderFunctionArgs } from "@remix-run/node";
import { v4 as uuidv4 } from "uuid";
import { authenticate, unauthenticated } from "../shopify.server";
import { runActivityCheck } from "../services/activity.server";
import { getActivityConfig } from "../services/config.server";
import { callPush, callStatus } from "../services/dreame-client.server";
import { findSuccessfulPush, recordSuccessfulPush } from "../services/idempotency.server";
import { logActivityPush } from "../services/logger.server";
import { getOrderDetails } from "../services/order.server";
const CORS_HEADERS = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "Authorization, Content-Type",
"Access-Control-Allow-Methods": "POST, OPTIONS",
};
type ActivityRequestBody = {
orderId?: string;
orderNumber?: string;
locale?: string;
};
export async function loader({ request }: LoaderFunctionArgs) {
if (request.method === "OPTIONS") {
return new Response(null, { status: 204, headers: CORS_HEADERS });
}
return json({ success: false }, { status: 405, headers: CORS_HEADERS });
}
export async function action({ request }: ActionFunctionArgs) {
if (request.method === "OPTIONS") {
return new Response(null, { status: 204, headers: CORS_HEADERS });
}
if (request.method !== "POST") {
return json({ success: false }, { status: 405, headers: CORS_HEADERS });
}
try {
const checkoutAuth = (authenticate as unknown as { public?: { checkout?: (request: Request) => Promise<unknown> } })
.public?.checkout;
if (!checkoutAuth) {
throw new Error("Shopify checkout authentication is unavailable");
}
const authResult = (await checkoutAuth(request)) as {
shop?: string;
admin?: unknown;
sessionToken?: { dest?: string; iss?: string };
session?: { shop?: string };
};
const shop = authResult.shop || authResult.session?.shop || shopFromSessionToken(authResult.sessionToken);
const adminResult = authResult.admin ? { admin: authResult.admin } : shop ? await unauthenticated.admin(shop) : null;
const admin = adminResult?.admin as {
graphql: (query: string, options?: { variables?: Record<string, unknown> }) => Promise<Response>;
};
if (!shop || !admin) {
throw new Error("Invalid Shopify checkout session");
}
const body = (await request.json()) as ActivityRequestBody;
if (!body.orderId) {
return json({ success: false }, { headers: CORS_HEADERS });
}
const result = await runActivityCheck(
{
shop,
orderId: body.orderId,
orderNumber: body.orderNumber,
locale: body.locale,
},
{
nowMs: () => Date.now(),
uuid: uuidv4,
getOrder: (_shop, orderId) => getOrderDetails(admin, orderId),
getConfig: () => getActivityConfig(admin),
findSuccess: findSuccessfulPush,
recordSuccess: recordSuccessfulPush,
log: logActivityPush,
callStatus,
callPush,
},
);
return json(result, { headers: CORS_HEADERS });
} catch {
return json({ success: false }, { headers: CORS_HEADERS });
}
}
function shopFromSessionToken(sessionToken: { dest?: string; iss?: string } | undefined): string | undefined {
const source = sessionToken?.dest || sessionToken?.iss;
if (!source) {
return undefined;
}
try {
return new URL(source).hostname;
} catch {
return source.replace(/^https?:\/\//, "").split("/")[0];
}
}