Refactor to store sync state in redis

This commit is contained in:
Dominic Ferrando
2026-06-23 14:24:01 -04:00
parent b4623a1224
commit 5bae0f067d
2 changed files with 46 additions and 32 deletions
+5 -3
View File
@@ -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);
+40 -28
View File
@@ -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;
}
};
} }