Refactor to store sync state in redis
This commit is contained in:
@@ -105,10 +105,11 @@ export const app = new Elysia()
|
|||||||
.group("/sync", (app) =>
|
.group("/sync", (app) =>
|
||||||
app
|
app
|
||||||
// Sync status
|
// Sync status
|
||||||
.get("/", async ({ }) => SyncService.state)
|
.get("/", async ({ }) => await SyncService.getState())
|
||||||
// Full sync
|
// Full sync
|
||||||
.post("/", async ({ }) => {
|
.post("/", async ({ }) => {
|
||||||
if (SyncService.state.isSyncing)
|
const syncState = await SyncService.getState();
|
||||||
|
if (syncState.isSyncing)
|
||||||
return status(409, { error: "Sync already in progress" });
|
return status(409, { error: "Sync already in progress" });
|
||||||
|
|
||||||
await SyncService.sync();
|
await SyncService.sync();
|
||||||
@@ -116,7 +117,8 @@ export const app = new Elysia()
|
|||||||
})
|
})
|
||||||
// Per-product sync
|
// Per-product sync
|
||||||
.post("/:printfulProductId", async ({ params }) => {
|
.post("/:printfulProductId", async ({ params }) => {
|
||||||
if (SyncService.state.isSyncing)
|
const syncState = await SyncService.getState();
|
||||||
|
if (syncState.isSyncing)
|
||||||
return status(409, { error: "Sync already in progress" });
|
return status(409, { error: "Sync already in progress" });
|
||||||
|
|
||||||
await SyncService.sync(params.printfulProductId);
|
await SyncService.sync(params.printfulProductId);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { Printful, Webflow } from "./util/types";
|
|||||||
import { FetchError, formatSlug, type DeepPartial } from "./util/misc";
|
import { FetchError, formatSlug, type DeepPartial } from "./util/misc";
|
||||||
import { WebflowService } from "./webflow";
|
import { WebflowService } from "./webflow";
|
||||||
import { PrintfulService } from "./printful";
|
import { PrintfulService } from "./printful";
|
||||||
|
import { redis } from "bun";
|
||||||
|
|
||||||
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
|
||||||
@@ -18,14 +19,14 @@ type MainProduct = {
|
|||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export class SyncService {
|
type SyncState = {
|
||||||
static state = {
|
isSyncing: boolean;
|
||||||
isSyncing: false,
|
syncingIds: number[];
|
||||||
syncingIds: [] as number[],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export abstract class SyncService {
|
||||||
static async sync(printfulProductId?: number) {
|
static async sync(printfulProductId?: number) {
|
||||||
this.state.isSyncing = true;
|
await this.setState({ isSyncing: true, syncingIds: [] });
|
||||||
|
|
||||||
// Populate products
|
// Populate products
|
||||||
const mainPrintfulProducts: MainProduct[] = [];
|
const mainPrintfulProducts: MainProduct[] = [];
|
||||||
@@ -79,8 +80,7 @@ export class SyncService {
|
|||||||
mainPrintfulProduct.colors.add(productColor);
|
mainPrintfulProduct.colors.add(productColor);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.state.isSyncing = false;
|
await this.setState({ isSyncing: false, syncingIds: [] });
|
||||||
this.state.syncingIds = [];
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,10 +90,12 @@ export class SyncService {
|
|||||||
console.log("Syncing main products...");
|
console.log("Syncing main products...");
|
||||||
for (const mainPrintfulProduct of mainPrintfulProducts) {
|
for (const mainPrintfulProduct of mainPrintfulProducts) {
|
||||||
try {
|
try {
|
||||||
this.state.isSyncing = true;
|
await this.setState({
|
||||||
this.state.syncingIds = mainPrintfulProduct.variants.map(
|
isSyncing: true,
|
||||||
|
syncingIds: mainPrintfulProduct.variants.map(
|
||||||
(v) => v.product.sync_product.id,
|
(v) => v.product.sync_product.id,
|
||||||
);
|
)
|
||||||
|
});
|
||||||
|
|
||||||
const foundColors = mainPrintfulProduct.colors;
|
const foundColors = mainPrintfulProduct.colors;
|
||||||
const foundSizes: Set<string> = new Set();
|
const foundSizes: Set<string> = new Set();
|
||||||
@@ -313,12 +315,36 @@ export class SyncService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.state.isSyncing = false;
|
await this.setState({ isSyncing: false, syncingIds: [] });
|
||||||
this.state.syncingIds = [];
|
|
||||||
console.log("Done");
|
console.log("Done");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static getProductImageUrls = async (slug: string) => {
|
public static async setState(state: SyncState) {
|
||||||
|
await redis.set("commerce:sync:state", JSON.stringify(state));
|
||||||
|
await redis.expire("commerce:sync:state", 1800);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async getState(): Promise<SyncState> {
|
||||||
|
const val = await redis.get("commerce:sync:state");
|
||||||
|
if (val) return JSON.parse(val);
|
||||||
|
return { isSyncing: false, syncingIds: [] }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static findColorInProductName(productName: string): string {
|
||||||
|
const m = productName.match(/\[([^\]]+)\]/);
|
||||||
|
return m?.[1] ? m[1] : "N/A";
|
||||||
|
};
|
||||||
|
|
||||||
|
public static getMainProductName(productName: string) {
|
||||||
|
const colorInName = this.findColorInProductName(productName);
|
||||||
|
if (colorInName) {
|
||||||
|
return productName.replace(`[${colorInName}]`, "").trimEnd();
|
||||||
|
} else {
|
||||||
|
return productName;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static async getProductImageUrls(slug: string) {
|
||||||
const res = await fetch(`${R2_WORKER_URL}/product-images/${slug}`);
|
const res = await fetch(`${R2_WORKER_URL}/product-images/${slug}`);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
console.error(
|
console.error(
|
||||||
@@ -330,18 +356,4 @@ export class SyncService {
|
|||||||
const productImageKeys: string[] = (await res.json()) as string[];
|
const productImageKeys: string[] = (await res.json()) as string[];
|
||||||
return productImageKeys.map((key) => `${WEBSITE_MEDIA_URL}/${key}`);
|
return productImageKeys.map((key) => `${WEBSITE_MEDIA_URL}/${key}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
public static findColorInProductName = (productName: string): string => {
|
|
||||||
const m = productName.match(/\[([^\]]+)\]/);
|
|
||||||
return m?.[1] ? m[1] : "N/A";
|
|
||||||
};
|
|
||||||
|
|
||||||
public static getMainProductName = (productName: string) => {
|
|
||||||
const colorInName = this.findColorInProductName(productName);
|
|
||||||
if (colorInName) {
|
|
||||||
return productName.replace(`[${colorInName}]`, "").trimEnd();
|
|
||||||
} else {
|
|
||||||
return productName;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user