Rebasely Beta: data may be purged

examples/storefront Public

TypeScript · 74 lines · 3.1 KiB · at 49aabbc

// The storefront's HTTP API. createApp takes the environment and the few things tests replace// (the clock and id generation) and returns a fetch handler.import { createCart, getCart, setQuantity } from "./carts.ts";import { getProduct, listProducts, parseProductInput, upsertProduct } from "./catalog.ts";import type { Env } from "./env.ts";import { errorResponse, HttpError, json, readJson, requireAdmin } from "./http.ts";import { putProductImage, serveImage } from "./images.ts";import { Router } from "./router.ts";export type Deps = {  now: () => number;  newId: () => string;};export const defaultDeps: Deps = { now: Date.now, newId: () => crypto.randomUUID() };export function createApp(env: Env, deps: Deps = defaultDeps) {  const r = new Router();  r.on("GET", "/products", async () => json({ products: await listProducts(env.DB) }));  r.on("GET", "/products/:id", async (_req, p) => {    const product = await getProduct(env.DB, p.id!);    if (!product || !product.active) throw new HttpError(404, "not_found", "No such product.");    return json({ product });  });  r.on("PUT", "/admin/products/:id", async (req, p) => {    requireAdmin(req, env.ADMIN_TOKEN);    const input = parseProductInput(p.id!, await readJson(req));    return json({ product: await upsertProduct(env.DB, input, deps.now()) });  });  r.on("PUT", "/admin/products/:id/image", async (req, p) => {    requireAdmin(req, env.ADMIN_TOKEN);    await putProductImage(env.DB, env.IMAGES, p.id!, req, deps.now());    return json({ product: await getProduct(env.DB, p.id!) });  });  r.on("GET", "/images/*", (req, p) => serveImage(env.IMAGES, p.rest!, req));  r.on("POST", "/carts", async () => json({ cart: await createCart(env.DB, deps.newId(), deps.now()) }, 201));  r.on("GET", "/carts/:id", async (_req, p) => {    const cart = await getCart(env.DB, p.id!);    if (!cart) throw new HttpError(404, "cart_not_found", "No such cart.");    return json({ cart });  });  r.on("PUT", "/carts/:id/items/:product", async (req, p) => {    const body = await readJson(req);    return json({ cart: await setQuantity(env.DB, p.id!, p.product!, body.quantity, deps.now()) });  });  r.on("DELETE", "/carts/:id/items/:product", async (_req, p) => json({ cart: await setQuantity(env.DB, p.id!, p.product!, 0, deps.now()) }));  return {    async fetch(req: Request): Promise<Response> {      const url = new URL(req.url);      try {        const hit = r.match(req.method, url.pathname);        if (hit === null) throw new HttpError(404, "not_found", "No such route.");        if (hit === "method_not_allowed") throw new HttpError(405, "method_not_allowed", `${req.method} is not allowed here.`);        return await hit.handler(req, hit.params);      } catch (e) {        if (e instanceof HttpError) return errorResponse(e);        if (e instanceof URIError) return errorResponse(new HttpError(400, "bad_path", "The path is not valid."));        console.error(JSON.stringify({ msg: "unhandled error", path: url.pathname, error: String(e) }));        return errorResponse(new HttpError(500, "internal", "Something went wrong."));      }    },  };}