latest
This commit is contained in:
+2
-93
@@ -1,95 +1,4 @@
|
||||
import { treaty } from "@elysiajs/eden";
|
||||
import { Elysia } from "elysia";
|
||||
import { cors } from "@elysiajs/cors";
|
||||
import { LevelCalculator, Standards, type ActivityStandards } from "$lib/services/calculator/main";
|
||||
import type { ActivityPerformance, Player } from "$lib/services/calculator/util";
|
||||
import rawStandards from "$lib/data/standards.json" assert { type: "json" }
|
||||
import { Printful } from "./services/commerce/util/types";
|
||||
import WebflowService from "./services/commerce/webflow";
|
||||
import SyncService from "./services/commerce/sync";
|
||||
import PrintfulService from "./services/commerce/printful";
|
||||
|
||||
export const app = new Elysia({ prefix: "/api" })
|
||||
.use(cors({
|
||||
origin: '*'
|
||||
}))
|
||||
|
||||
.get("/", () => "Hello world")
|
||||
|
||||
// CALCULATOR
|
||||
|
||||
.post("/calculate", async ({ body }) => {
|
||||
const MAIN_STANDARDS = new Standards(rawStandards as ActivityStandards);
|
||||
interface CalcRequest {
|
||||
player: Player;
|
||||
activityPerformances: ActivityPerformance[];
|
||||
}
|
||||
const { player, activityPerformances } = body as CalcRequest;
|
||||
const levelCalculator = new LevelCalculator(MAIN_STANDARDS);
|
||||
return { levels: levelCalculator.calculate(player, activityPerformances) };
|
||||
}, {
|
||||
error({ error }) {
|
||||
console.error(error);
|
||||
return {
|
||||
message: "Failed to calculate",
|
||||
error
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// COMMERCE
|
||||
.group("/products", app => app
|
||||
.group("/sync", app => app
|
||||
.get("/", async ({ }) => SyncService.state)
|
||||
.post("/:printfulProductId?", async ({ params }) => {
|
||||
const printfulProductId = params.printfulProductId ?
|
||||
+params.printfulProductId :
|
||||
undefined;
|
||||
await SyncService.sync(printfulProductId);
|
||||
}, {
|
||||
error({ error }) {
|
||||
console.error(error);
|
||||
return {
|
||||
message: "Failed to sync product(s)",
|
||||
error
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
.get("/:printfulProductId", async ({ params }) => {
|
||||
const printfulProductId = +params.printfulProductId;
|
||||
const printfulProduct = await PrintfulService.Products.get(printfulProductId);
|
||||
|
||||
const webflowProductId = printfulProduct.sync_product.external_id.split("-")[0];
|
||||
const webflowProduct = await WebflowService.Products.get(webflowProductId);
|
||||
|
||||
return { printfulProduct, webflowProduct };
|
||||
})
|
||||
)
|
||||
|
||||
// WEBHOOKS
|
||||
.post("/webhook/printful", async ({ body }) => {
|
||||
const payload = body as Printful.Webhook.EventPayload;
|
||||
switch (payload.type) {
|
||||
case Printful.Webhook.Event.ProductUpdated: {
|
||||
const printfulProduct = payload.data.sync_product;
|
||||
await SyncService.sync(printfulProduct.id);
|
||||
break;
|
||||
}
|
||||
case Printful.Webhook.Event.ProductDeleted: {
|
||||
await WebflowService.Products.remove(payload.data.sync_product.external_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, {
|
||||
error({ error }) {
|
||||
console.error(error);
|
||||
return {
|
||||
message: "Failed to execute webhook",
|
||||
error
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export const api = treaty<typeof app>("localhost:5173").api;
|
||||
import type { App } from "./server/types";
|
||||
|
||||
export const api = treaty<App>('http://localhost:5173').api;
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import { Elysia, NotFoundError } from "elysia";
|
||||
import { cors } from "@elysiajs/cors";
|
||||
import { LevelCalculator, Standards, type ActivityStandards } from "$lib/services/calculator/main";
|
||||
import type { ActivityPerformance, Player } from "$lib/services/calculator/util";
|
||||
import rawStandards from "$lib/data/standards.json" assert { type: "json" }
|
||||
import { Printful } from "../services/commerce/util/types";
|
||||
import WebflowService from "../services/commerce/webflow";
|
||||
import SyncService from "../services/commerce/sync";
|
||||
import PrintfulService from "../services/commerce/printful";
|
||||
import { FetchError } from "../services/commerce/util/misc";
|
||||
|
||||
export const app = new Elysia({ prefix: "/api" })
|
||||
.use(cors({ origin: '*' }))
|
||||
|
||||
.error({
|
||||
FetchError
|
||||
})
|
||||
|
||||
.onError(({ code, error }) => {
|
||||
switch (code) {
|
||||
case "FetchError":
|
||||
console.error(error.message, error.payload);
|
||||
return error;
|
||||
}
|
||||
})
|
||||
|
||||
.get("/", () => "Hello world")
|
||||
|
||||
// CALCULATOR
|
||||
|
||||
.post("/calculate", async ({ body }) => {
|
||||
const MAIN_STANDARDS = new Standards(rawStandards as ActivityStandards);
|
||||
interface CalcRequest {
|
||||
player: Player;
|
||||
activityPerformances: ActivityPerformance[];
|
||||
}
|
||||
const { player, activityPerformances } = body as CalcRequest;
|
||||
const levelCalculator = new LevelCalculator(MAIN_STANDARDS);
|
||||
return { levels: levelCalculator.calculate(player, activityPerformances) };
|
||||
})
|
||||
|
||||
// COMMERCE
|
||||
.group("/products", app => app
|
||||
.group("/sync", app => app
|
||||
.get("/", async ({ }) => SyncService.state)
|
||||
.post("/:printfulProductId?", async ({ params }) => {
|
||||
const printfulProductId = params.printfulProductId ?
|
||||
+params.printfulProductId :
|
||||
undefined;
|
||||
await SyncService.sync(printfulProductId);
|
||||
})
|
||||
)
|
||||
.get("/:printfulProductId", async ({ params }) => {
|
||||
const printfulProductId = +params.printfulProductId;
|
||||
const printfulProduct = await PrintfulService.Products.get(printfulProductId);
|
||||
if (!printfulProduct) {
|
||||
return new NotFoundError();
|
||||
}
|
||||
|
||||
const webflowProductId = printfulProduct.sync_product.external_id.split("-")[0];
|
||||
const webflowProduct = await WebflowService.Products.get(webflowProductId);
|
||||
|
||||
return { printfulProduct, webflowProduct };
|
||||
})
|
||||
)
|
||||
|
||||
// WEBHOOKS
|
||||
.post("/webhook/printful", async ({ body }) => {
|
||||
const payload = body as Printful.Webhook.EventPayload;
|
||||
switch (payload.type) {
|
||||
case Printful.Webhook.Event.ProductUpdated: {
|
||||
const printfulProduct = payload.data.sync_product;
|
||||
await SyncService.sync(printfulProduct.id);
|
||||
break;
|
||||
}
|
||||
case Printful.Webhook.Event.ProductDeleted: {
|
||||
await WebflowService.Products.remove(payload.data.sync_product.external_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,2 @@
|
||||
import type { app } from './app';
|
||||
export type App = typeof app;
|
||||
@@ -1,8 +1,28 @@
|
||||
import type { Printful } from "./util/types";
|
||||
import { type DeepPartial } from "./util/misc";
|
||||
import { FetchError, type DeepPartial } from "./util/misc";
|
||||
|
||||
export default class PrintfulService {
|
||||
static Products = class {
|
||||
static Variants = class {
|
||||
static async get(variantId: number | string): Promise<Printful.Products.SyncVariant | undefined> {
|
||||
const res = await fetch(`${env().API_URL}/store/variants/${variantId}`, {
|
||||
method: "GET",
|
||||
headers: { ...env().AUTH_HEADERS }
|
||||
});
|
||||
|
||||
if (res.status === 404 || res.status === 400) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
throw await FetchError.createAndParse("Failed to get Printful variant", res);
|
||||
}
|
||||
|
||||
const payload: Printful.Products.MetaDataSingle<Printful.Products.SyncVariant> = await res.json();
|
||||
return payload.result;
|
||||
}
|
||||
}
|
||||
|
||||
static async getAll(offset: number = 0): Promise<Printful.Products.SyncProduct[]> {
|
||||
const allSyncProducts: Printful.Products.SyncProduct[] = [];
|
||||
|
||||
@@ -14,8 +34,7 @@ export default class PrintfulService {
|
||||
})
|
||||
|
||||
if (!res.ok) {
|
||||
console.log(await res.json());
|
||||
throw new Error("Failed to get all Printful products",);
|
||||
throw await FetchError.createAndParse("Failed to get all Printful products", res);
|
||||
}
|
||||
|
||||
const payload: Printful.Products.MetaDataMulti<Printful.Products.SyncProduct> = await res.json();
|
||||
@@ -26,23 +45,25 @@ export default class PrintfulService {
|
||||
if (allSyncProducts.length >= payload.paging.total)
|
||||
break;
|
||||
} catch (error) {
|
||||
console.error("Printful product get all failed:", error);
|
||||
throw new Error("Failed to get all Printful products",);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return allSyncProducts;
|
||||
}
|
||||
|
||||
static async get(printfulProductId: number): Promise<Printful.Products.Product> {
|
||||
const res = await fetch(`${env().API_URL}/store/products/${printfulProductId}`, {
|
||||
static async get(productId: number | string): Promise<Printful.Products.Product | undefined> {
|
||||
const res = await fetch(`${env().API_URL}/store/products/${productId}`, {
|
||||
method: "GET",
|
||||
headers: { ...env().AUTH_HEADERS }
|
||||
});
|
||||
|
||||
if (res.status === 404 || res.status === 400) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
console.error("Printful product get failed:", await res.json());
|
||||
throw new Error("Failed to get Printful product");
|
||||
throw await FetchError.createAndParse("Failed to get Printful product", res);
|
||||
}
|
||||
|
||||
const payload: Printful.Products.MetaDataSingle<Printful.Products.Product> = await res.json();
|
||||
@@ -60,8 +81,7 @@ export default class PrintfulService {
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
console.error("Printful product update failed:", await res.json());
|
||||
throw new Error("Failed to update Printful product");
|
||||
throw await FetchError.createAndParse("Failed to update Printful product", printfulProduct.sync_product?.name, res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Printful, Webflow } from "./util/types";
|
||||
import { formatSlug, type DeepPartial } from "./util/misc";
|
||||
import { FetchError, formatSlug, type DeepPartial } from "./util/misc";
|
||||
import WebflowService from "./webflow";
|
||||
import PrintfulService from "./printful";
|
||||
|
||||
@@ -25,8 +25,10 @@ export default class SyncService {
|
||||
}
|
||||
|
||||
static async sync(printfulProductId: number | undefined) {
|
||||
console.log("Syncing...");
|
||||
this.state.isSyncing = true;
|
||||
|
||||
const mainPrintfulProducts: MainProduct[] = [];
|
||||
const webflowProducts: Webflow.Products.ProductAndSkus[] = [];
|
||||
try {
|
||||
let printfulProducts = await PrintfulService.Products.getAll();
|
||||
if (printfulProductId) {
|
||||
@@ -36,11 +38,10 @@ export default class SyncService {
|
||||
printfulProducts = printfulProducts.filter(p => p.name.includes(mainProductName));
|
||||
}
|
||||
}
|
||||
const webflowProducts = await WebflowService.Products.getAll();
|
||||
webflowProducts.push(...await WebflowService.Products.getAll());
|
||||
|
||||
// Generate main printful products
|
||||
console.log("Generating main products...");
|
||||
const mainPrintfulProducts: MainProduct[] = [];
|
||||
for (const printfulProduct of printfulProducts) {
|
||||
const mainProductName = this.getMainProductName(printfulProduct.name);
|
||||
|
||||
@@ -59,15 +60,25 @@ export default class SyncService {
|
||||
const productColor = this.findColorInProductName(printfulProduct.name);
|
||||
mainPrintfulProduct.variants.push({
|
||||
color: productColor,
|
||||
product: await PrintfulService.Products.get(printfulProduct.id)
|
||||
product: (await PrintfulService.Products.get(printfulProduct.id))!
|
||||
});
|
||||
mainPrintfulProduct.colors.add(productColor);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
throw error;
|
||||
}
|
||||
finally {
|
||||
this.state.isSyncing = false;
|
||||
this.state.syncingIds = [];
|
||||
}
|
||||
|
||||
// TODO: Validate main printful products
|
||||
// TODO: Validate main printful products
|
||||
|
||||
// Sync the main printful products
|
||||
for (const mainPrintfulProduct of mainPrintfulProducts) {
|
||||
// Sync the main printful products
|
||||
console.log("Syncing main products...");
|
||||
for (const mainPrintfulProduct of mainPrintfulProducts) {
|
||||
try {
|
||||
this.state.isSyncing = true;
|
||||
this.state.syncingIds = mainPrintfulProduct.variants.map(v => v.product.sync_product.id);
|
||||
|
||||
@@ -115,6 +126,12 @@ export default class SyncService {
|
||||
.find(webflowProduct => webflowProduct.product.id === mainPrintfulProduct.externalId.split("-")[0]);
|
||||
if (existingWebflowProduct) {
|
||||
// SYNC PRODUCT UPDATE
|
||||
|
||||
// do not update images
|
||||
for (const webflowSku of webflowSkus) {
|
||||
delete webflowSku.fieldData?.["main-image"];
|
||||
}
|
||||
|
||||
const webflowProductId = mainPrintfulProduct.externalId.split("-")[0];
|
||||
WebflowService.Products.update(webflowProductId, {
|
||||
"product": {
|
||||
@@ -225,28 +242,29 @@ export default class SyncService {
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SYNC IMAGES
|
||||
for (const sku of existingWebflowProduct.skus) {
|
||||
const skuColor = sku.fieldData["sku-values"]?.["color"]?.toLowerCase() ?? "None";
|
||||
const skuSlug = `${existingWebflowProduct.product.fieldData.slug}-${skuColor}`;
|
||||
const skuImageUrls = await this.getProductImageUrls(skuSlug);
|
||||
|
||||
sku.fieldData["main-image"] = skuImageUrls[0] ?? sku.fieldData["main-image"];
|
||||
sku.fieldData["more-images"] = skuImageUrls.slice(1).map(url => ({ url }));
|
||||
|
||||
await WebflowService.Products.Skus.update(existingWebflowProduct.product.id, sku.id, sku);
|
||||
// SYNC IMAGES
|
||||
// for (const sku of existingWebflowProduct.skus) {
|
||||
// const skuColor = sku.fieldData["sku-values"]?.["color"]?.toLowerCase() ?? "None";
|
||||
// const skuSlug = `${existingWebflowProduct.product.fieldData.slug}-${skuColor}`;
|
||||
// const skuImageUrls = await this.getProductImageUrls(skuSlug);
|
||||
//
|
||||
// sku.fieldData["main-image"] = skuImageUrls[0] ?? sku.fieldData["main-image"];
|
||||
// sku.fieldData["more-images"] = skuImageUrls.slice(1).map(url => ({ url }));
|
||||
//
|
||||
// await WebflowService.Products.Skus.update(existingWebflowProduct.product.id, sku.id, sku);
|
||||
// }
|
||||
//
|
||||
catch (err) {
|
||||
if (err instanceof FetchError) {
|
||||
console.error(err.message, err.payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
finally {
|
||||
this.state.isSyncing = false;
|
||||
this.state.syncingIds = [];
|
||||
}
|
||||
this.state.isSyncing = false;
|
||||
this.state.syncingIds = [];
|
||||
console.log("Done");
|
||||
}
|
||||
|
||||
private static getProductImageUrls = async (slug: string) => {
|
||||
|
||||
@@ -11,3 +11,25 @@ export const formatSlug = (name: string): string => {
|
||||
.replace(/--+/g, '-') // collapse multiple dashes
|
||||
.replace(/^([^a-z0-9_])/, '_$1'); // if it starts with an invalid char, prefix underscore
|
||||
};
|
||||
|
||||
export class FetchError extends Error {
|
||||
public payload: Object | undefined;
|
||||
|
||||
constructor(message: string, public response: Response) {
|
||||
super(message);
|
||||
this.name = "FetchError";
|
||||
}
|
||||
|
||||
static async createAndParse(message: string, response: Response): Promise<FetchError> {
|
||||
const fetchError = new FetchError(message, response);
|
||||
try {
|
||||
fetchError.payload = await fetchError.response.clone().json();
|
||||
} catch {
|
||||
fetchError.payload = await fetchError.response.text().catch(() => undefined);
|
||||
}
|
||||
finally {
|
||||
return fetchError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Webflow } from "./util/types";
|
||||
import { type DeepPartial } from "./util/misc";
|
||||
import { FetchError, type DeepPartial } from "./util/misc";
|
||||
|
||||
export default class WebflowService {
|
||||
static Products = class {
|
||||
@@ -17,8 +17,7 @@ export default class WebflowService {
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
console.error("Webflow product SKU create failed:", await res.json());
|
||||
throw new Error("Failed to create Webflow product SKU");
|
||||
throw await FetchError.createAndParse("Failed to create Webflow product SKU", res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,8 +33,7 @@ export default class WebflowService {
|
||||
})
|
||||
});
|
||||
if (!res.ok) {
|
||||
console.error("Webflow product SKU update failed:", await res.json());
|
||||
throw new Error("Failed to update Webflow product SKU");
|
||||
throw await FetchError.createAndParse("Failed to update Webflow product SKU", res);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,9 +49,7 @@ export default class WebflowService {
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
console.error("Webflow product create failed:", res.statusText);
|
||||
console.log(await res.json());
|
||||
throw new Error("Failed to create Webflow product");
|
||||
throw await FetchError.createAndParse("Failed to create Webflow product", res);
|
||||
}
|
||||
|
||||
const createdWebflowProduct = await res.json();
|
||||
@@ -69,8 +65,7 @@ export default class WebflowService {
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
console.error("Webflow product get all failed:", res.statusText);
|
||||
throw new Error("Failed to get all Webflow products");
|
||||
throw await FetchError.createAndParse("Failed to get all Webflow products", res);
|
||||
}
|
||||
|
||||
const payload = await res.json();
|
||||
@@ -90,8 +85,7 @@ export default class WebflowService {
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
console.error("Webflow product get failed:", res.statusText);
|
||||
throw new Error("Failed to get Webflow product");
|
||||
throw await FetchError.createAndParse("Failed to get Webflow product", res);
|
||||
}
|
||||
|
||||
const payload = await res.json();
|
||||
@@ -109,8 +103,7 @@ export default class WebflowService {
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
console.error("Webflow product update failed:", await res.json());
|
||||
throw new Error("Failed to update Webflow product");
|
||||
throw await FetchError.createAndParse("Failed to update Webflow product", res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,8 +118,7 @@ export default class WebflowService {
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
console.error("Webflow product remove failed:", res.statusText);
|
||||
throw new Error("Failed to remove Webflow product");
|
||||
throw await FetchError.createAndParse("Failed to remove Webflow product", res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Activity, ftToCm, Gender, getAvgWeight, inToCm, kgToLb, lbToKg, minToMs, secToMs, type ActivityPerformance, type Player } from "$lib/services/calculator/util";
|
||||
import PrintfulService from "$lib/services/commerce/printful";
|
||||
import SyncService from "$lib/services/commerce/sync";
|
||||
import type { DeepPartial } from "$lib/services/commerce/util/misc";
|
||||
import { FetchError, type DeepPartial } from "$lib/services/commerce/util/misc";
|
||||
import type { Printful } from "$lib/services/commerce/util/types";
|
||||
import WebflowService from "$lib/services/commerce/webflow";
|
||||
|
||||
@@ -46,64 +46,73 @@ const computedPerformances: ActivityPerformance[] = [
|
||||
},
|
||||
];
|
||||
|
||||
// const res = await fetch(`${Webflow.API_SITES_URL}/products/${}/skus/${"68d0154f97e90776a5680740"}`, {
|
||||
// method: "PATCH",
|
||||
// headers: {
|
||||
// "Content-Type": "application/json",
|
||||
// "Authorization": `bearer ${Bun.env.WEBFLOW_AUTH}`
|
||||
// },
|
||||
// body: JSON.stringify({
|
||||
// "sku": webflowVariants[i]
|
||||
// })
|
||||
// });
|
||||
// wProduct.product.fieldData.name = "Rogue Title Hoodie [White] / XS";
|
||||
//
|
||||
// WebflowService.Products.update("68e66dc4633e577c91eda727",);
|
||||
|
||||
const p = await WebflowService.Products.get("68e66dc4633e577c91eda713");
|
||||
console.log(p);
|
||||
const printfulProducts = await PrintfulService.Products.getAll();
|
||||
const webflowProducts = await WebflowService.Products.getAll();
|
||||
|
||||
for (const printfulProduct of printfulProducts) {
|
||||
if (!printfulProduct.name.includes("T-Shirt") || printfulProduct.name.includes("[White]") || printfulProduct.name.includes("[Black]")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(printfulProduct.name);
|
||||
|
||||
const fullPrintfulProduct = await PrintfulService.Products.get(printfulProduct.id);
|
||||
if (!fullPrintfulProduct) {
|
||||
console.log("Could not get full product, skipped");
|
||||
continue;
|
||||
}
|
||||
|
||||
const existingWebflowProduct = webflowProducts.find(p => fullPrintfulProduct.sync_product.name.includes(p.product.fieldData.name))
|
||||
if (!existingWebflowProduct) {
|
||||
console.log("Could not get associated webflow product, skipped");
|
||||
continue;
|
||||
}
|
||||
|
||||
const newPrintfulVariants: DeepPartial<Printful.Products.SyncVariant>[] = [];
|
||||
for (const printfulVariant of fullPrintfulProduct.sync_variants) {
|
||||
console.log(printfulVariant.name);
|
||||
const associatedWebflowSku = existingWebflowProduct.skus
|
||||
.find(sku => sku.fieldData["sku-values"]?.["color"] === SyncService.findColorInProductName(printfulVariant.name) &&
|
||||
sku.fieldData["sku-values"]?.["size"] === printfulVariant.size);
|
||||
if (associatedWebflowSku) {
|
||||
console.log('Found associated webflow sku: ' + associatedWebflowSku.fieldData.name);
|
||||
newPrintfulVariants.push({
|
||||
"id": printfulVariant.id, // printful variant id
|
||||
"external_id": String(associatedWebflowSku.id), // webflow variant id
|
||||
});
|
||||
}
|
||||
else {
|
||||
console.log("Could not get associated webflow sku");
|
||||
}
|
||||
}
|
||||
|
||||
if (!newPrintfulVariants.length) {
|
||||
console.log("No new printful variants, skipped");
|
||||
continue;
|
||||
}
|
||||
|
||||
await sleep(3000);
|
||||
try {
|
||||
await PrintfulService.Products.update(printfulProduct.id, {
|
||||
"sync_product": {
|
||||
"id": printfulProduct.id,
|
||||
"external_id": existingWebflowProduct.product.id + "-" + SyncService.findColorInProductName(printfulProduct.name)
|
||||
},
|
||||
"sync_variants": newPrintfulVariants
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof FetchError) {
|
||||
console.error(err.message, err.payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log("DONE");
|
||||
|
||||
// const printfulProducts = await PrintfulService.Products.getAll();
|
||||
// const webflowProducts = await WebflowService.Products.getAll();
|
||||
//
|
||||
// for (const printfulProduct of printfulProducts) {
|
||||
// const fullPrintfulProduct = await PrintfulService.Products.get(printfulProduct.id);
|
||||
// console.log(fullPrintfulProduct.sync_product.name);
|
||||
// const existingWebflowProduct = webflowProducts.find(p => fullPrintfulProduct.sync_product.name.includes(p.product.fieldData.name))
|
||||
// if (!existingWebflowProduct) {
|
||||
// continue;
|
||||
// }
|
||||
// console.log(existingWebflowProduct.product.fieldData.name);
|
||||
//
|
||||
// console.log(existingWebflowProduct.skus.map(s => s.fieldData["sku-values"]));
|
||||
// console.log(fullPrintfulProduct.sync_variants.map(s => ({ color: s.color, size: s.size })));
|
||||
//
|
||||
// const newPrintfulVariants: DeepPartial<Printful.Products.SyncVariant>[] = [];
|
||||
// for (const printfulVariant of fullPrintfulProduct.sync_variants) {
|
||||
// const associatedWebflowSku = existingWebflowProduct.skus
|
||||
// .find(sku => sku.fieldData["sku-values"]?.["color"] === printfulVariant.color &&
|
||||
// sku.fieldData["sku-values"]?.["size"] === printfulVariant.size);
|
||||
// if (associatedWebflowSku) {
|
||||
// newPrintfulVariants.push({
|
||||
// "id": printfulVariant.id, // printful variant id
|
||||
// "external_id": String(associatedWebflowSku.id), // webflow variant id
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (!newPrintfulVariants.length) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// await sleep(10000);
|
||||
// await PrintfulService.Products.update(printfulProduct.id, {
|
||||
// "sync_product": {
|
||||
// "id": printfulProduct.id,
|
||||
// "external_id": existingWebflowProduct.product.id + "-" + SyncService.findColorInProductName(printfulProduct.name)
|
||||
// },
|
||||
// "sync_variants": newPrintfulVariants
|
||||
// });
|
||||
// }
|
||||
// console.log("DONE");
|
||||
|
||||
//
|
||||
// await WebflowService.Products.update(wProduct?.product.id!, wProduct!);
|
||||
//
|
||||
// const sku = {
|
||||
@@ -122,11 +131,11 @@ console.log(p);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
|
||||
|
||||
const skuId = "68d080a5d90e8263e52242e9";
|
||||
|
||||
const colors = ["Black", "White", "Red"];
|
||||
//
|
||||
//
|
||||
// const skuId = "68d080a5d90e8263e52242e9";
|
||||
//
|
||||
// const colors = ["Black", "White", "Red"];
|
||||
|
||||
// const sku = product.skus[0];
|
||||
//
|
||||
@@ -0,0 +1,69 @@
|
||||
import PrintfulService from "$lib/services/commerce/printful";
|
||||
import SyncService from "$lib/services/commerce/sync";
|
||||
import WebflowService from "$lib/services/commerce/webflow";
|
||||
|
||||
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
type Validation = {
|
||||
isValid: boolean,
|
||||
message: string
|
||||
}
|
||||
|
||||
const validateEquality = (wValue: any, pValue: any): Validation => {
|
||||
const validation = {
|
||||
isValid: true,
|
||||
message: "Valid",
|
||||
};
|
||||
|
||||
if (wValue !== pValue) {
|
||||
validation.isValid = false;
|
||||
validation.message = `${wValue} !== ${pValue}`
|
||||
};
|
||||
|
||||
return validation;
|
||||
}
|
||||
|
||||
|
||||
const wProducts = await WebflowService.Products.getAll();
|
||||
|
||||
let invalidCount = 0;
|
||||
for (const wProduct of wProducts) {
|
||||
for (const sku of wProduct.skus ?? []) {
|
||||
console.log("Webflow: " + sku.id, sku.fieldData.name);
|
||||
|
||||
const pVariant = await PrintfulService.Products.Variants.get(`@${sku.id}`);
|
||||
if (pVariant) {
|
||||
console.log("Printful: " + pVariant.id, pVariant.name);
|
||||
const validations = [
|
||||
// validateEquality(sku.fieldData.name, pVariant.name),
|
||||
validateEquality(sku.fieldData["sku-values"]?.["color"], SyncService.findColorInProductName(pVariant.name)),
|
||||
validateEquality(sku.fieldData["sku-values"]?.["size"], pVariant.size),
|
||||
validateEquality(sku.fieldData.price.value, Math.floor(+pVariant.retail_price * 100)),
|
||||
];
|
||||
|
||||
// if (!validateEquality(sku.fieldData.name, pVariant.name).isValid) {
|
||||
// sku.fieldData.name = pVariant.name;
|
||||
// await WebflowService.Products.Skus.update(wProduct.product.id, sku.id, sku);
|
||||
// }
|
||||
|
||||
const errors = validations.filter(v => !v.isValid);
|
||||
if (errors.length) {
|
||||
++invalidCount;
|
||||
console.log("INVALID: ");
|
||||
console.log(errors.map(e => e.message).join(", "));
|
||||
}
|
||||
else {
|
||||
console.log("VALID");
|
||||
}
|
||||
}
|
||||
else {
|
||||
++invalidCount;
|
||||
console.log("INVALID");
|
||||
console.log("Not found");
|
||||
}
|
||||
console.log();
|
||||
}
|
||||
await sleep(3000);
|
||||
}
|
||||
|
||||
console.log("INVALID COUNT: " + invalidCount);
|
||||
@@ -1,4 +1,4 @@
|
||||
import { app } from "$lib/api"
|
||||
import { app } from "$lib/server/app"
|
||||
|
||||
type RequestHandler = (v: { request: Request }) => Response | Promise<Response>
|
||||
export const fallback: RequestHandler = ({ request }) => app.handle(request)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { api } from "$lib/api.js";
|
||||
import type { Printful } from "$lib/services/commerce/util/types.js";
|
||||
import { api } from "$lib/api";
|
||||
import type { Printful } from "$lib/services/commerce/util/types";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
const { data } = $props();
|
||||
@@ -10,9 +10,10 @@
|
||||
syncingIds: [] as number[],
|
||||
poll: async function () {
|
||||
const res = await api.products.sync.get();
|
||||
|
||||
if (res.data) {
|
||||
synchronizer.isSyncing = res.data.isSyncing ?? false;
|
||||
synchronizer.syncingIds = res.data.syncingIds ?? [];
|
||||
synchronizer.isSyncing = res.data.isSyncing;
|
||||
synchronizer.syncingIds = res.data.syncingIds;
|
||||
}
|
||||
},
|
||||
startPolling: () => {
|
||||
|
||||
Reference in New Issue
Block a user