77 lines
2.6 KiB
TypeScript
77 lines
2.6 KiB
TypeScript
import { json, type RequestHandler } from '@sveltejs/kit';
|
|
import { StatusCodes } from 'http-status-codes';
|
|
import { getFbpFbcFromCookies } from '../metapixel/fbc.ts';
|
|
import type { CAPIConnector } from './connector.ts';
|
|
import { getRequestIP } from '../util/ip.ts';
|
|
import * as v from 'valibot';
|
|
import { CAPIEvent, MetaServerEventParamsSchema, type CAPICustomerInfoParams } from './event.ts';
|
|
|
|
export const capiRequestBodySchema = v.object({
|
|
events: v.array(MetaServerEventParamsSchema)
|
|
});
|
|
|
|
/** Request body for conversion events */
|
|
export type CAPIRequestBody = v.InferOutput<typeof capiRequestBodySchema>;
|
|
|
|
export const capiErrorBodySchema = v.object({
|
|
error: v.string()
|
|
});
|
|
|
|
/** Returned by the conversion request handler in case of an error */
|
|
export type CAPIErrorBody = v.InferOutput<typeof capiErrorBodySchema>;
|
|
|
|
export const capiResponseBodySchema = v.object({
|
|
pixelID: v.string(),
|
|
fbTraceID: v.string(),
|
|
receivedEvents: v.number(),
|
|
processedEvents: v.number(),
|
|
messages: v.array(v.string())
|
|
});
|
|
|
|
/** Returned by the conversion request handler in case of a successful response */
|
|
export type CAPIResponseBody = v.InferOutput<typeof capiResponseBodySchema>;
|
|
|
|
/**
|
|
* Creates a SvelteKit request handler for processing conversion events.
|
|
*
|
|
* @param connector - The CAPIConnector instance to send events through.
|
|
* @returns A SvelteKit RequestHandler function.
|
|
*/
|
|
export const createCAPIHandler: (connector: CAPIConnector) => RequestHandler = (connector) => {
|
|
const handle: RequestHandler = async ({ request, getClientAddress, cookies }) => {
|
|
try {
|
|
const json = await request.json();
|
|
const parsed = v.parse(capiRequestBodySchema, json);
|
|
|
|
// Build enriched user data with IP, user agent, and fbp/fbc from cookies
|
|
const ip = getRequestIP(request, getClientAddress);
|
|
const ua = request.headers.get('user-agent') ?? undefined;
|
|
const { fbp, fbc } = getFbpFbcFromCookies(cookies);
|
|
|
|
const enrichedUserData: Partial<CAPICustomerInfoParams> = {
|
|
clientIP: ip,
|
|
clientUserAgent: ua,
|
|
fbp,
|
|
fbc
|
|
};
|
|
|
|
// Enrich each event's user data
|
|
const events: CAPIEvent[] = parsed.events.map((eventParams) => {
|
|
const event = CAPIEvent.fromObject(eventParams);
|
|
event.enrichUserData(enrichedUserData);
|
|
return event;
|
|
});
|
|
|
|
// Send the event via the control
|
|
const response = await connector.sendEvents(events);
|
|
return json(response, { status: StatusCodes.OK });
|
|
} catch (e) {
|
|
const response: CAPIErrorBody = { error: e instanceof Error ? e.message : String(e) };
|
|
return json(response, {
|
|
status: StatusCodes.INTERNAL_SERVER_ERROR
|
|
});
|
|
}
|
|
};
|
|
return handle;
|
|
};
|