2) Storage helpers

server/storage/coverTemplates.ts

import crypto from "crypto";
import { db } from "../db"; // swap for your client

export type CoverTemplate = {
  id: string; creator_id?: string | null; title: string; category: string; tags?: string[];
  price_cents: number; currency: string; preview_image_url: string;
  pptx_url?: string | null; keynote_url?: string | null; gslides_url?: string | null;
  download_bundle_url?: string | null; is_active: boolean; created_at: string;
};

export async function listCoverTemplates(opts: { q?: string; category?: string; min?: number; max?: number } = {}) {
  let q = db("cover_templates").where({ is_active: true });
  if (opts.category) q = q.andWhere("category", opts.category);
  if (opts.min != null) q = q.andWhere("price_cents", ">=", opts.min);
  if (opts.max != null) q = q.andWhere("price_cents", "<=", opts.max);
  if (opts.q) q = q.andWhereILike("title", `%${opts.q}%`);
  return q.orderBy("created_at", "desc");
}

export async function getCoverTemplate(id: string) {
  return db("cover_templates").where({ id }).first();
}

export async function createCoverTemplate(input: Partial<CoverTemplate>) {
  const id = input.id ?? crypto.randomUUID();
  await db("cover_templates").insert({ id, ...input, is_active: false }); // default inactive (await approval)
  return await getCoverTemplate(id);
}

export async function updateCoverTemplate(id: string, patch: Partial<CoverTemplate>) {
  await db("cover_templates").where({ id }).update(patch);
  return await getCoverTemplate(id);
}

export async function createPurchase(rec: {
  user_id: string; template_id: string; custom_image_url?: string | null;
  amount_cents: number; currency: string; stripe_session_id?: string | null;
}) {
  const id = crypto.randomUUID();
  await db("cover_purchases").insert({
    id, ...rec, status: "pending", download_url: null, stripe_payment_intent: null
  });
  return await db("cover_purchases").where({ id }).first();
}

export async function markPurchasePaidBySession(sessionId: string, paymentIntent: string, downloadUrl: string) {
  await db("cover_purchases")
    .where({ stripe_session_id: sessionId })
    .update({ status: "paid", stripe_payment_intent: paymentIntent, download_url: downloadUrl });
  return await db("cover_purchases").where({ stripe_session_id: sessionId }).first();
}

export async function getUserPurchases(userId: string) {
  return db("cover_purchases").where({ user_id: userId }).orderBy("created_at", "desc");
}
