diff --git a/apps/api/src/database/migrations/2026-06-03.ts b/apps/api/src/database/migrations/2026-06-03.ts index b7692b2..cfb76c4 100644 --- a/apps/api/src/database/migrations/2026-06-03.ts +++ b/apps/api/src/database/migrations/2026-06-03.ts @@ -7,8 +7,8 @@ export async function up(db: Kysely): Promise { .$call(addDefaultColumns) .addColumn("session_id", "uuid", (cb) => cb.notNull()) .addColumn("session_name", "text", (cb) => cb.notNull()) - .addColumn("printful_product_id_filter", sql`integer[]`) - .addColumn("syncing_printful_product_ids", sql`integer[]`, (cb) => cb + .addColumn("p_product_id_filter", sql`integer[]`) + .addColumn("syncing_p_product_ids", sql`integer[]`, (cb) => cb .notNull() .defaultTo(sql`'{}'::integer[]`) ) diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts index dd89015..f398110 100644 --- a/apps/api/src/server.ts +++ b/apps/api/src/server.ts @@ -155,31 +155,31 @@ export const app = new Elysia() return { startDate: latestSync.started_at, status: s.Commerce.Sync.deriveStatus(latestSync), - syncingPrintfulProductIds: latestSync.syncing_printful_product_ids + syncingPProductIds: latestSync.syncing_p_product_ids } }) // Run sync - .post("/:printfulProductId?", async ({ params: { printfulProductId }, sessionId }) => { + .post("/:pProductId?", async ({ params: { pProductId }, sessionId }) => { await s.Commerce.Sync.start({ session: { id: sessionId, name: "Portal" }, filter: { - printfulProductIds: printfulProductId ? [printfulProductId] : null + pProductIds: pProductId ? [pProductId] : null } }); - }, { params: t.Object({ printfulProductId: t.Optional(t.Numeric()) }) }), + }, { params: t.Object({ pProductId: t.Optional(t.Numeric()) }) }), ) - .get("/:printfulProductId", async ({ params: { printfulProductId } }) => { - const printfulProduct = await s.Commerce.Printful.Products.get(printfulProductId); - if (!printfulProduct) throw new NotFoundError("Missing printful product"); + .get("/:pProductId", async ({ params: { pProductId } }) => { + const pProduct = await s.Commerce.Printful.Products.get(pProductId); + if (!pProduct) throw new NotFoundError("Missing printful product"); - const webflowProductId = printfulProduct.sync_product.external_id.split("-")[0]; - if (!webflowProductId) throw new NotFoundError("Missing webflow product ID"); + const wProductId = pProduct.sync_product.external_id.split("-")[0]; + if (!wProductId) throw new NotFoundError("Missing webflow product ID"); - const webflowProduct = await s.Commerce.Webflow.Products.get(webflowProductId); + const wProduct = await s.Commerce.Webflow.Products.get(wProductId); - return { printfulProduct, webflowProduct }; + return { pProduct, wProduct }; }, { - params: t.Object({ printfulProductId: t.Numeric() }) + params: t.Object({ pProductId: t.Numeric() }) }) ) @@ -190,37 +190,37 @@ 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"); + const pProduct = payload.data.sync_product; + log.info({ productId: pProduct.id }, "printful webhook: product updated"); await s.Commerce.Sync.start({ session: { id: randomUUIDv7(), name: "Printful" }, filter: { - printfulProductIds: [printfulProduct.id] + pProductIds: [pProduct.id] } }); break; } case Printful.Webhook.Event.ProductDeleted: { - const printfulProduct = payload.data.sync_product; - const webflowProductId = printfulProduct.external_id.split("-")[0]; - log.info({ externalId: payload.data.sync_product.external_id, webflowProductId }, "printful webhook: product deleted"); - if (!webflowProductId) throw new NotFoundError("Missing webflow product ID"); + const pProduct = payload.data.sync_product; + const wProductId = pProduct.external_id.split("-")[0]; + log.info({ externalId: payload.data.sync_product.external_id, wProductId }, "printful webhook: product deleted"); + if (!wProductId) throw new NotFoundError("Missing webflow product ID"); - await s.Commerce.Webflow.Products.remove(webflowProductId); + await s.Commerce.Webflow.Products.remove(wProductId); break; } case Printful.Webhook.Event.PackageShipped: { - const webflowOrderId = payload.data.order.external_id; + const wOrderId = payload.data.order.external_id; const shipInfo = payload.data.shipment; - log.info({ webflowOrderId, carrier: shipInfo.carrier, tracking: shipInfo.tracking_number }, "printful webhook: package shipped"); + log.info({ wOrderId, carrier: shipInfo.carrier, tracking: shipInfo.tracking_number }, "printful webhook: package shipped"); - await s.Commerce.Webflow.Orders.update(webflowOrderId, { + await s.Commerce.Webflow.Orders.update(wOrderId, { shippingTrackingURL: shipInfo.tracking_url, shippingTracking: shipInfo.tracking_number, shippingProvider: shipInfo.carrier, }); - await s.Commerce.Webflow.Orders.fulfill(webflowOrderId, { + await s.Commerce.Webflow.Orders.fulfill(wOrderId, { sendOrderFulfilledEmail: true, }); break; @@ -237,27 +237,27 @@ 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"); + const wOrder = payload.payload; + log.info({ orderId: wOrder.orderId }, "webflow webhook: order created"); await s.Commerce.Printful.Orders.create({ - external_id: webflowOrder.orderId, + external_id: wOrder.orderId, // TODO: derive from webflow shipping: "STANDARD", recipient: { - name: webflowOrder.shippingAddress.addressee, - address1: webflowOrder.shippingAddress.line1, - address2: webflowOrder.shippingAddress.line2, - city: webflowOrder.shippingAddress.city, + name: wOrder.shippingAddress.addressee, + address1: wOrder.shippingAddress.line1, + address2: wOrder.shippingAddress.line2, + city: wOrder.shippingAddress.city, state_code: zipcodesUs.find( - webflowOrder.shippingAddress.postalCode.split("-")[0] ?? "", + wOrder.shippingAddress.postalCode.split("-")[0] ?? "", ).stateCode, - country_code: webflowOrder.shippingAddress.country, - zip: webflowOrder.shippingAddress.postalCode, + country_code: wOrder.shippingAddress.country, + zip: wOrder.shippingAddress.postalCode, }, - items: webflowOrder.purchasedItems.map((webflowOrderSku) => ({ - external_variant_id: webflowOrderSku.variantId, - quantity: webflowOrderSku.count, + items: wOrder.purchasedItems.map((wOrderSku) => ({ + external_variant_id: wOrderSku.variantId, + quantity: wOrderSku.count, })), }); diff --git a/apps/api/src/services/commerce.ts b/apps/api/src/services/commerce.ts index bea9c25..1b8a468 100644 --- a/apps/api/src/services/commerce.ts +++ b/apps/api/src/services/commerce.ts @@ -52,7 +52,7 @@ class SyncService { session_id: queuedSync.session.id, session_name: queuedSync.session.name, started_at: startDate, - printful_product_id_filter: queuedSync.filter?.printfulProductIds ?? null, + p_product_id_filter: queuedSync.filter?.pProductIds ?? null, }; if (id) { const result = await db.updateTable("product_syncs") @@ -73,17 +73,17 @@ class SyncService { id = result.id; } }, - onStep: async (printfulProductIds: number[]) => { + onStep: async (pProductIds: number[]) => { if (!id) throw new Error("Expected sync run ID"); await db.updateTable("product_syncs") - .set({ syncing_printful_product_ids: printfulProductIds }) + .set({ syncing_p_product_ids: pProductIds }) .where("id", "=", id) .execute(); }, onCompletion: async (completionDate, duration) => { if (!id) throw new Error("Expected sync run ID"); await db.updateTable("product_syncs") - .set({ syncing_printful_product_ids: [], ended_at: completionDate }) + .set({ syncing_p_product_ids: [], ended_at: completionDate }) .where("id", "=", id) .execute(); freedSlot = true; @@ -104,7 +104,7 @@ class SyncService { if (Date.now() - (activeSync.started_at?.getTime() ?? 0) > 600000) { log.info("already active sync exceeded timeout"); await db.updateTable("product_syncs") - .set({ syncing_printful_product_ids: [], ended_at: new Date(), has_failed: true }) + .set({ syncing_p_product_ids: [], ended_at: new Date(), has_failed: true }) .where("id", "=", activeSync.id) .execute(); await this.start(queuedSync); @@ -117,7 +117,7 @@ class SyncService { else { if (id) { await db.updateTable("product_syncs") - .set({ syncing_printful_product_ids: [], ended_at: new Date(), has_failed: true }) + .set({ syncing_p_product_ids: [], ended_at: new Date(), has_failed: true }) .where("id", "=", id) .execute(); freedSlot = true; @@ -173,7 +173,7 @@ class SyncQueueService { const values: Insertable = { session_id: queuedSync.session.id, session_name: queuedSync.session.name, - printful_product_id_filter: queuedSync.filter?.printfulProductIds ?? null + p_product_id_filter: queuedSync.filter?.pProductIds ?? null }; if (queuedSync.id) { await db.updateTable("product_syncs") @@ -204,7 +204,7 @@ class SyncQueueService { name: result.session_name }, filter: { - printfulProductIds: result.printful_product_id_filter + pProductIds: result.p_product_id_filter } } } diff --git a/apps/portal/src/routes/(app)/commerce/+page.svelte b/apps/portal/src/routes/(app)/commerce/+page.svelte index 3b9d431..3d61528 100644 --- a/apps/portal/src/routes/(app)/commerce/+page.svelte +++ b/apps/portal/src/routes/(app)/commerce/+page.svelte @@ -9,7 +9,7 @@ const synchronizer = $state({ status: "none" as SyncStatus, - syncingPrintfulProductIds: [] as number[], + syncingPProductIds: [] as number[], get isInProgress() { return this.status === "active" || this.status === "queued"; }, @@ -17,11 +17,11 @@ const res = await api.products.sync.get(); if (res.error) { synchronizer.status = "none"; - synchronizer.syncingPrintfulProductIds = []; + synchronizer.syncingPProductIds = []; } else { synchronizer.status = res.data.status; - synchronizer.syncingPrintfulProductIds = - res.data.syncingPrintfulProductIds; + synchronizer.syncingPProductIds = + res.data.syncingPProductIds; } }, startPolling: () => { @@ -34,9 +34,9 @@ synchronizer.startPolling(); await api.products.sync.post(); }, - sync: async (printfulProductId: number) => { + sync: async (pProductId: number) => { synchronizer.startPolling(); - await api.products.sync({ printfulProductId }).post(); + await api.products.sync({ pProductId }).post(); }, }); @@ -48,18 +48,18 @@ }); const isProductSynced = async ( - printfulProduct: Printful.Products.SyncProduct, + pProduct: Printful.Products.SyncProduct, ) => { - const webflowProducts = await data.products.webflow; - return webflowProducts.some( - (p) => p.product.id === printfulProduct.external_id.split("-")[0], + const wProducts = await data.products.webflow; + return wProducts.some( + (p) => p.product.id === pProduct.external_id.split("-")[0], ); }; {#await data.products.printful}

Loading...

-{:then printfulProducts} +{:then pProducts} - {#await isProductSynced(printfulProduct) then isSynced} - {#if synchronizer.syncingPrintfulProductIds.includes(printfulProduct.id)} + {#await isProductSynced(pProduct) then isSynced} + {#if synchronizer.syncingPProductIds.includes(pProduct.id)}
Syncing...
@@ -120,7 +120,7 @@