Setting up more printful logic. Setting up db.

This commit is contained in:
Dominic Ferrando
2025-05-03 13:54:55 -04:00
parent 6cd2d72fc5
commit bb7c041c48
8 changed files with 233 additions and 1 deletions
+3
View File
@@ -1 +1,4 @@
api
node_modules/
.env.*
./database/data.db
+19
View File
@@ -0,0 +1,19 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"devDependencies": {
"@types/bun": "^1.2.11",
},
},
},
"packages": {
"@types/bun": ["@types/bun@1.2.11", "", { "dependencies": { "bun-types": "1.2.11" } }, "sha512-ZLbbI91EmmGwlWTRWuV6J19IUiUC5YQ3TCEuSHI3usIP75kuoA8/0PVF+LTrbEnVc8JIhpElWOxv1ocI1fJBbw=="],
"@types/node": ["@types/node@22.15.3", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw=="],
"bun-types": ["bun-types@1.2.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-dbkp5Lo8HDrXkLrONm6bk+yiiYQSntvFUzQp0v3pzTAsXk6FtgVMjdQ+lzFNVAmQFUkPQZ3WMZqH5tTo+Dp/IA=="],
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
}
}
BIN
View File
Binary file not shown.
+5
View File
@@ -0,0 +1,5 @@
CREATE TABLE IF NOT EXISTS products (
webflowProductId INTEGER NOT NULL,
printfulProductId INTEGER NOT NULL,
UNIQUE(webflowProductId, printfulProductId)
)
+33 -1
View File
@@ -1 +1,33 @@
console.log("Hello world");
import { Printful } from "./printful.ts"
import { ProductRecords } from "./storage.ts"
const productRecords = new ProductRecords();
const server = Bun.serve({
routes: {
"/webhook/printful": {
async POST(req) {
const payload: Printful.Webhook.EventPayload = await req.json()
switch (payload.type) {
case Printful.Webhook.Event.ProductUpdated:
const productRecord = productRecords.findFromPrintful(payload.data.sync_product.id)
if (productRecord) {
}
else {
}
break;
case Printful.Webhook.Event.ProductDeleted:
break;
}
return Response.json("Hello world")
}
}
},
development: true
})
console.log(`Listening on ${server.url}`);
+9
View File
@@ -0,0 +1,9 @@
{
"dependencies": {},
"devDependencies": {
"@types/bun": "^1.2.11"
},
"scripts": {
"db:seed": "sqlite3 ./database/data.db < ./database/seed.sql"
}
}
+122
View File
@@ -0,0 +1,122 @@
export namespace Printful {
const API_ROUTE = "https://api.printful.com/store";
export namespace Webhook {
// meta
interface MetaData {
created: number;
retries: number;
store: number;
}
export enum Event {
ProductUpdated = "product_updated",
ProductDeleted = "product_deleted"
}
// product updated
export interface ProductUpdated extends MetaData {
type: Event.ProductUpdated,
data: {
sync_product: {
id: number;
external_id: string;
name: string;
variants: number;
synced: number;
thumbnail_url: string;
is_ignored: boolean;
};
}
}
// product deleted
export interface ProductDeleted extends MetaData {
type: Event.ProductDeleted,
data: {
sync_product: {
id: number;
external_id: string;
name: string;
};
}
}
export type EventPayload = ProductUpdated | ProductDeleted
}
export namespace Products {
interface MetaData<T = any> {
code: number;
result: T;
}
interface Option {
id: string;
value: string;
}
interface File {
type: string;
id: number;
url: string;
options: Option[];
hash: string;
filename: string;
mime_type: string;
size: number;
width: number;
height: number;
dpi: number;
status: string;
created: number;
thumbnail_url: string;
preview_url: string;
visible: boolean;
is_temporary: boolean;
stitch_count_tier: string;
}
interface SyncProductData {
id: number;
external_id: string;
sync_product_id: number;
name: string;
synced: boolean;
variant_id: number;
retail_price: string;
currency: string;
is_ignored: boolean;
sku: string;
product: {
variant_id: number;
product_id: number;
image: string;
name: string;
};
files: File[];
options: Option[];
main_category_id: number;
warehouse_product_id: number;
warehouse_product_variant_id: number;
size: string;
color: string;
availability_status: string;
}
export type SyncProduct = MetaData<SyncProductData>;
}
export async function getSyncProduct(syncProductId: String): Promise<Products.SyncProduct> {
const payload: Products.SyncProduct = await (
await fetch(`${API_ROUTE}/products/${syncProductId}`, {
method: "GET",
headers: {
"Authorization": `Bearer ${Bun.env.PRINTFUL_AUTH}`,
"X-PF-Store-Id": `${Bun.env.PRINTFUL_STORE_ID}`
}
})
).json();
return payload;
}
}
+42
View File
@@ -0,0 +1,42 @@
import { Database } from "bun:sqlite";
interface ProductsRecord {
webflowProductId: number;
printfulProductId: number;
}
export class ProductRecords {
private db: Database;
constructor() {
this.db = new Database("./database/data.db");
}
findFromWebflow(webflowProductId: number): ProductsRecord | undefined {
const row = this.db.query(`
SELECT * FROM product_records WHERE webflowProductId = ?
`).get(webflowProductId);
return row as ProductsRecord | undefined;
}
findFromPrintful(printfulProductId: number): ProductsRecord | undefined {
const row = this.db.query(`
SELECT * FROM product_records WHERE printfulProductId = ?
`).get(printfulProductId);
return row as ProductsRecord | undefined;
}
add(record: ProductsRecord): void {
this.db.run(`
INSERT OR IGNORE INTO product_records (webflowProductId, printfulProductId)
VALUES (?, ?)
`, [record.webflowProductId, record.printfulProductId]);
}
all(): ProductsRecord[] {
return this.db.query(`SELECT * FROM product_records`).all() as ProductsRecord[];
}
}