From 15aa6e44f095cf1ef3d24abc72a4d15c954ccaab Mon Sep 17 00:00:00 2001 From: Dominic Ferrando Date: Sat, 18 Oct 2025 08:27:51 -0400 Subject: [PATCH] latest --- src/lib/api.ts | 7 ++- src/lib/server/app.ts | 61 +++++++++++++++++++++++---- src/lib/services/calculator/main.ts | 4 +- src/lib/services/commerce/printful.ts | 29 +++++++++++++ src/lib/test/scratch.ts | 5 +-- vite.config.ts | 2 +- 6 files changed, 93 insertions(+), 15 deletions(-) diff --git a/src/lib/api.ts b/src/lib/api.ts index 5fce29b..446f7fc 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -1,4 +1,9 @@ import { treaty } from "@elysiajs/eden"; import type { App } from "./server/types"; -export const api = treaty('http://localhost:5173').api; +const baseURL = + import.meta.env.PROD + ? "https://blade-and-brawn.fly.dev" + : "http://localhost:5173"; + +export const api = treaty(baseURL).api; diff --git a/src/lib/server/app.ts b/src/lib/server/app.ts index 5c12a22..bcc6364 100644 --- a/src/lib/server/app.ts +++ b/src/lib/server/app.ts @@ -1,6 +1,6 @@ import { Elysia, NotFoundError } from "elysia"; import { cors } from "@elysiajs/cors"; -import { LevelCalculator, Standards, type ActivityStandards } from "$lib/services/calculator/main"; +import { LevelCalculator, Standards, type ActivityStandards, type LevelCalculatorOutput } 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, Webflow } from "../services/commerce/util/types"; @@ -9,8 +9,24 @@ import SyncService from "../services/commerce/sync"; import PrintfulService from "../services/commerce/printful"; import { FetchError, getStateFromZip } from "../services/commerce/util/misc"; +const MAIN_STANDARDS = new Standards(rawStandards as ActivityStandards); +const levelCalculator = new LevelCalculator(MAIN_STANDARDS); + export const app = new Elysia({ prefix: "/api" }) - .use(cors({ origin: '*' })) + .use( + cors({ + origin: [ + // bladeandbrawn.com (apex + any subdomain) + /^https?:\/\/([a-z0-9-]+\.)?bladeandbrawn\.com$/i, + // Webflow preview domains + /^https?:\/\/([a-z0-9-]+\.)?bladeandbrawn\.webflow\.io$/i, + // Fly app host (this one already works) + /^https?:\/\/blade-and-brawn\.fly\.dev$/i, + // Local dev + "http://localhost:5173", + ] + }) + ) .error({ FetchError @@ -24,30 +40,55 @@ export const app = new Elysia({ prefix: "/api" }) } }) + .onAfterHandle(({ request, set }) => { + console.log(JSON.stringify({ + lvl: "info", + msg: "req", + method: request.method, + path: new URL(request.url).pathname, + status: set.status ?? 200 + })); + }) + .get("/", () => "Hello world") // CALCULATOR - .post("/calculate", async ({ body }) => { - const MAIN_STANDARDS = new Standards(rawStandards as ActivityStandards); + .post("/calculate", async ({ body, query }) => { interface CalcRequest { player: Player; activityPerformances: ActivityPerformance[]; } const { player, activityPerformances } = body as CalcRequest; - const levelCalculator = new LevelCalculator(MAIN_STANDARDS); - return { levels: levelCalculator.calculate(player, activityPerformances) }; + const output = { levels: levelCalculator.calculate(player, activityPerformances) }; + + console.log({ log: query?.log }); + if (query?.log === "true") { + void fetch("https://kv-logger.xominus.workers.dev", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ output, player, activityPerformances }) + }).catch(e => { }); + } + + return output; }) // COMMERCE + // TODO: protect with api key/token .group("/products", app => app .group("/sync", app => app .get("/", async ({ }) => SyncService.state) - .post("/:printfulProductId?", async ({ params }) => { + .post("/:printfulProductId?", async ({ params, set }) => { + if (SyncService.state.isSyncing) { + set.status = 409; + return { error: "Sync already in progress" }; + } const printfulProductId = params.printfulProductId ? +params.printfulProductId : undefined; await SyncService.sync(printfulProductId); + return { ok: true }; }) ) .get("/:printfulProductId", async ({ params }) => { @@ -68,8 +109,6 @@ export const app = new Elysia({ prefix: "/api" }) .post("/webhook/printful", async ({ body }) => { const payload = body as Printful.Webhook.EventPayload; - console.log("Hit webhook", payload.type); - switch (payload.type) { case Printful.Webhook.Event.ProductUpdated: { const printfulProduct = payload.data.sync_product; @@ -93,6 +132,8 @@ export const app = new Elysia({ prefix: "/api" }) break; } } + + return { ok: true }; }) .post("/webhook/webflow", async ({ request, body, set }) => { if (!WebflowService.Util.verifyWebflowSignature(request, body)) { @@ -130,4 +171,6 @@ export const app = new Elysia({ prefix: "/api" }) break; } } + + return { ok: true }; }); diff --git a/src/lib/services/calculator/main.ts b/src/lib/services/calculator/main.ts index 40f1874..374d783 100644 --- a/src/lib/services/calculator/main.ts +++ b/src/lib/services/calculator/main.ts @@ -198,7 +198,9 @@ export class Standards { 0.1, weightSkew: 0, ageModifier: 0.5, - difficultyModifier: 0 + difficultyModifier: activity === Activity.BroadJump ? + 0.05 : + 0 }, ]) ) as Record; diff --git a/src/lib/services/commerce/printful.ts b/src/lib/services/commerce/printful.ts index 723c081..5392a37 100644 --- a/src/lib/services/commerce/printful.ts +++ b/src/lib/services/commerce/printful.ts @@ -101,6 +101,35 @@ export default class PrintfulService { throw new FetchError("Failed to create printful order", res); } } + + static async getAll(offset: number = 0): Promise { + const allPrintfulOrders: Printful.Orders.Order[] = []; + + while (true) { + try { + const res = await fetch(`${env().API_URL}/orders?offset=${offset}`, { + method: "GET", + headers: { ...env().AUTH_HEADERS } + }) + + if (!res.ok) { + throw new FetchError("Failed to get all Printful orders", res); + } + + const payload: Printful.Products.MetaDataMulti = await res.json(); + + allPrintfulOrders.push(...payload.result); + offset = allPrintfulOrders.length; + + if (allPrintfulOrders.length >= payload.paging.total) + break; + } catch (error) { + throw error; + } + } + + return allPrintfulOrders; + } } static Util = class { diff --git a/src/lib/test/scratch.ts b/src/lib/test/scratch.ts index 85b44a3..ae7e348 100644 --- a/src/lib/test/scratch.ts +++ b/src/lib/test/scratch.ts @@ -46,9 +46,8 @@ const computedPerformances: ActivityPerformance[] = [ }, ]; -const wOrders = await WebflowService.Orders.getAll(); - -console.log(wOrders); +const pOrders = await PrintfulService.Orders.getAll(); +console.log(pOrders); // wProduct.product.fieldData.name = "Rogue Title Hoodie [White] / XS"; // diff --git a/vite.config.ts b/vite.config.ts index 7bc709f..3ca802f 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,5 +3,5 @@ import { sveltekit } from '@sveltejs/kit/vite'; import { defineConfig } from 'vite'; export default defineConfig({ - plugins: [tailwindcss(), sveltekit()], + plugins: [tailwindcss(), sveltekit()] });