Overhaul logging
This commit is contained in:
+8
-1
@@ -4,11 +4,18 @@ function requireEnv(key: string): string {
|
||||
return val;
|
||||
}
|
||||
|
||||
function optionEnv(key: string, fallback: string): string {
|
||||
const val = Bun.env[key];
|
||||
if (!val) return fallback;
|
||||
return val;
|
||||
}
|
||||
|
||||
export const env = {
|
||||
PRINTFUL_AUTH: requireEnv("PRINTFUL_AUTH"),
|
||||
PRINTFUL_STORE_ID: requireEnv("PRINTFUL_STORE_ID"),
|
||||
WEBFLOW_SITE_ID: requireEnv("WEBFLOW_SITE_ID"),
|
||||
WEBFLOW_COLLECTION_ID: requireEnv("WEBFLOW_COLLECTION_ID"),
|
||||
WEBFLOW_AUTH: requireEnv("WEBFLOW_AUTH"),
|
||||
WEBFLOW_WEBHOOK_SECRET: requireEnv("WEBFLOW_WEBHOOK_SECRET")
|
||||
WEBFLOW_WEBHOOK_SECRET: requireEnv("WEBFLOW_WEBHOOK_SECRET"),
|
||||
LOG_LEVEL: optionEnv("LOG_LEVEL", "info")
|
||||
};
|
||||
|
||||
+41
-34
@@ -21,8 +21,11 @@ import {
|
||||
} from "@blade-and-brawn/commerce";
|
||||
import zipcodesUs from "zipcodes-us";
|
||||
import z from "zod";
|
||||
import pino from "pino";
|
||||
import { env } from "./env";
|
||||
|
||||
const log = pino({ level: env.LOG_LEVEL });
|
||||
|
||||
// SERVICES
|
||||
// -----------
|
||||
const levelCalculator = new LevelCalculator(
|
||||
@@ -41,11 +44,15 @@ const webflow = new WebflowClient({
|
||||
webhookSecret: env.WEBFLOW_WEBHOOK_SECRET,
|
||||
});
|
||||
|
||||
const productSyncer = new ProductSyncer(printful, webflow);
|
||||
const productSyncer = new ProductSyncer(printful, webflow, log);
|
||||
|
||||
// ELYSIA
|
||||
// -----------
|
||||
export const app = new Elysia()
|
||||
.derive(() => ({
|
||||
startTime: Date.now(),
|
||||
requestId: crypto.randomUUID(),
|
||||
}))
|
||||
.use(
|
||||
cors({
|
||||
origin: [
|
||||
@@ -71,27 +78,29 @@ export const app = new Elysia()
|
||||
WebflowError,
|
||||
})
|
||||
|
||||
.onError(({ code, error }) => {
|
||||
.onError(({ code, error, startTime, requestId }) => {
|
||||
const duration = Date.now() - (startTime ?? Date.now());
|
||||
switch (code) {
|
||||
case "PrintfulError":
|
||||
case "WebflowError":
|
||||
console.error(error.message, error.status, error.payload);
|
||||
log.error(
|
||||
{ requestId, duration, upstreamStatus: error.status, payload: error.payload },
|
||||
error.message,
|
||||
);
|
||||
return status(502, { error: error.message });
|
||||
default:
|
||||
console.error(error);
|
||||
log.error({ requestId, duration, err: error }, "unhandled error");
|
||||
}
|
||||
})
|
||||
|
||||
.onAfterHandle(({ request, set }) => {
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
lvl: "info",
|
||||
msg: "req",
|
||||
method: request.method,
|
||||
path: new URL(request.url).pathname,
|
||||
status: set.status ?? 200,
|
||||
}),
|
||||
);
|
||||
.onAfterHandle(({ request, set, startTime, requestId }) => {
|
||||
log.info({
|
||||
requestId,
|
||||
method: request.method,
|
||||
path: new URL(request.url).pathname,
|
||||
status: set.status ?? 200,
|
||||
duration: Date.now() - startTime,
|
||||
}, "request");
|
||||
})
|
||||
|
||||
.get("/", () => ({ status: "ok" }))
|
||||
@@ -148,18 +157,14 @@ export const app = new Elysia()
|
||||
await productSyncer.sync(params.printfulProductId);
|
||||
}, { params: z.object({ printfulProductId: z.coerce.number() }) }),
|
||||
)
|
||||
// TODO: add schema validation
|
||||
.get("/:printfulProductId", async ({ params }) => {
|
||||
const printfulProduct =
|
||||
await printful.Products.get(params.printfulProductId);
|
||||
const printfulProduct = await printful.Products.get(params.printfulProductId);
|
||||
if (!printfulProduct) throw new NotFoundError("Missing printful product");
|
||||
|
||||
const webflowProductId =
|
||||
printfulProduct.sync_product.external_id.split("-")[0];
|
||||
const webflowProductId = printfulProduct.sync_product.external_id.split("-")[0];
|
||||
if (!webflowProductId) throw new NotFoundError("Missing webflow product ID");
|
||||
|
||||
const webflowProduct =
|
||||
await webflow.Products.get(webflowProductId);
|
||||
const webflowProduct = await webflow.Products.get(webflowProductId);
|
||||
|
||||
return { printfulProduct, webflowProduct };
|
||||
}, {
|
||||
@@ -174,18 +179,19 @@ export const app = new Elysia()
|
||||
switch (payload.type) {
|
||||
case Printful.Webhook.Event.ProductUpdated: {
|
||||
const printfulProduct = payload.data.sync_product;
|
||||
log.info({ productId: printfulProduct.id }, "printful webhook: product updated");
|
||||
await productSyncer.sync(printfulProduct.id);
|
||||
break;
|
||||
}
|
||||
case Printful.Webhook.Event.ProductDeleted: {
|
||||
await webflow.Products.remove(
|
||||
payload.data.sync_product.external_id,
|
||||
);
|
||||
log.info({ externalId: payload.data.sync_product.external_id }, "printful webhook: product deleted");
|
||||
await webflow.Products.remove(payload.data.sync_product.external_id);
|
||||
break;
|
||||
}
|
||||
case Printful.Webhook.Event.PackageShipped: {
|
||||
const webflowOrderId = payload.data.order.external_id;
|
||||
const shipInfo = payload.data.shipment;
|
||||
log.info({ webflowOrderId, carrier: shipInfo.carrier, tracking: shipInfo.tracking_number }, "printful webhook: package shipped");
|
||||
|
||||
await webflow.Orders.update(webflowOrderId, {
|
||||
shippingTrackingURL: shipInfo.tracking_url,
|
||||
@@ -197,6 +203,8 @@ export const app = new Elysia()
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
log.warn({ type: (payload as any).type }, "printful webhook: unhandled event type");
|
||||
}
|
||||
})
|
||||
.post("/webhook/webflow", async ({ request, body }) => {
|
||||
@@ -208,6 +216,7 @@ export const app = new Elysia()
|
||||
switch (payload.triggerType) {
|
||||
case Webflow.Webhook.Event.OrderCreated: {
|
||||
const webflowOrder = payload.payload;
|
||||
log.info({ orderId: webflowOrder.orderId }, "webflow webhook: order created");
|
||||
|
||||
await printful.Orders.create({
|
||||
external_id: webflowOrder.orderId,
|
||||
@@ -219,24 +228,22 @@ export const app = new Elysia()
|
||||
address2: webflowOrder.shippingAddress.line2,
|
||||
city: webflowOrder.shippingAddress.city,
|
||||
state_code: zipcodesUs.find(
|
||||
webflowOrder.shippingAddress.postalCode.split(
|
||||
"-",
|
||||
)[0] ?? "",
|
||||
webflowOrder.shippingAddress.postalCode.split("-")[0] ?? "",
|
||||
).stateCode,
|
||||
country_code: webflowOrder.shippingAddress.country,
|
||||
zip: webflowOrder.shippingAddress.postalCode,
|
||||
},
|
||||
items: webflowOrder.purchasedItems.map(
|
||||
(webflowOrderSku) => ({
|
||||
external_variant_id: webflowOrderSku.variantId,
|
||||
quantity: webflowOrderSku.count,
|
||||
}),
|
||||
),
|
||||
items: webflowOrder.purchasedItems.map((webflowOrderSku) => ({
|
||||
external_variant_id: webflowOrderSku.variantId,
|
||||
quantity: webflowOrderSku.count,
|
||||
})),
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
log.warn({ triggerType: (payload as any).triggerType }, "webflow webhook: unhandled event type");
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
app.listen(3000, () => log.info({ port: 3000 }, "server started"));
|
||||
|
||||
Reference in New Issue
Block a user