From c287b825ec901caf3444b3cafa71a310b0bb5725 Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Tue, 7 Jul 2026 07:50:42 +0000 Subject: [PATCH] Serve a default house ad when no paid campaign is available serveAd() returned null when a slot had no eligible/in-budget campaign, leaving publisher units blank. Now it falls back to a CrawlProof house ad so a slot is never empty: - lib/ads/house.ts: renders an image-based house ad from default AI-generated promo artwork (public/ads/house/*.webp) with an overlaid "Your ad here" pitch linking back to CrawlProof. Not metered or billed. - Wired into serveAd for both the no-creative and out-of-budget cases; invalid/ inactive slots and format mismatches still return null. Co-Authored-By: Claude Opus 4.8 --- lib/ads/house.ts | 96 +++++++++++++++++++++++++++++++ lib/ads/serve.ts | 7 ++- public/ads/house/promo-rect.webp | Bin 0 -> 7788 bytes public/ads/house/promo-wide.webp | Bin 0 -> 10072 bytes 4 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 lib/ads/house.ts create mode 100644 public/ads/house/promo-rect.webp create mode 100644 public/ads/house/promo-wide.webp diff --git a/lib/ads/house.ts b/lib/ads/house.ts new file mode 100644 index 00000000..50472bf7 --- /dev/null +++ b/lib/ads/house.ts @@ -0,0 +1,96 @@ +import crypto from "node:crypto"; +import { env } from "@/lib/env"; +import { formatSpec, type AdCreative, type AdFormatId } from "./creative"; +import type { Fill } from "./serve"; + +// Default "house" ad for the CrawlProof Ad Network. Shown when a slot has no +// eligible paid campaign, so a publisher's unit is never blank. Uses default +// AI-generated promo artwork (public/ads/house/*) with an overlaid pitch that +// links back to CrawlProof. House ads are not metered or billed. + +const HOUSE = { + headline: "Your ad here", + body: "Crypto pay-per-click ads for indie sites — no middlemen.", + cta: "Advertise →", +} as const; + +function imageUrlFor(format: AdFormatId): string { + const rect = format === "banner_300x250"; + return `${env.siteUrl}/ads/house/${rect ? "promo-rect" : "promo-wide"}.webp`; +} + +function esc(s: string): string { + return s.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); +} + +export function renderHouseAdHtml(format: AdFormatId, clickUrl: string): string { + const { w, h } = formatSpec(format); + const img = imageUrlFor(format); + const isMobile = format === "banner_320x50"; + const isRect = format === "banner_300x250"; + const row = !isRect; // leaderboard + mobile are horizontal + + const label = `CrawlProof Ads`; + const headline = `
${esc(HOUSE.headline)}
`; + const body = isMobile + ? "" + : `
${esc(HOUSE.body)}
`; + const cta = `${esc(HOUSE.cta)}`; + + const content = row + ? `
+
${headline}${body}
+
${cta}
+
` + : `
+
${headline}${body}
+
${cta}
+
`; + + const overlay = isRect + ? "linear-gradient(180deg, rgba(7,10,16,.12) 0%, rgba(7,10,16,.86) 76%)" + : "linear-gradient(90deg, rgba(7,10,16,.92) 0%, rgba(7,10,16,.5) 62%, rgba(7,10,16,.2) 100%)"; + + return ` + +
${label}${content} +
+ `; +} + +function houseCreative(format: AdFormatId): AdCreative { + return { + format, + headline: HOUSE.headline, + body: HOUSE.body, + ctaText: HOUSE.cta, + bgColor: "#070a10", + fgColor: "#eef3f8", + accentColor: "#6ee7b7", + fontFamily: "system-ui, -apple-system, Segoe UI, Roboto, sans-serif", + logoUrl: null, + imageUrl: imageUrlFor(format), + }; +} + +/** A default house-ad fill promoting the CrawlProof Ad Network. Not metered. */ +export function houseFill(format: AdFormatId): Fill { + const clickUrl = `${env.siteUrl}/?utm_source=house-ad&utm_medium=ad&utm_campaign=crawlproof-ads`; + return { + impressionId: crypto.randomUUID(), + campaignId: "house", + creativeId: "house", + refSlug: "house", + creative: houseCreative(format), + clickUrl, + html: renderHouseAdHtml(format, clickUrl), + }; +} diff --git a/lib/ads/serve.ts b/lib/ads/serve.ts index 1d85a0cc..ae3fd127 100644 --- a/lib/ads/serve.ts +++ b/lib/ads/serve.ts @@ -8,6 +8,7 @@ import { type AdCreative, type AdFormatId, } from "./creative"; +import { houseFill } from "./house"; import { CPC_CENTS, CPC_CREDITS, PLATFORM_RATE } from "./pricing"; // Server-side ad selection + metering. Runs under the service-role client so @@ -101,7 +102,8 @@ export async function serveAd( .eq("ad_campaigns.status", "active") .limit(100); - if (!creatives || creatives.length === 0) return null; + // No paid creative for this format → default CrawlProof house ad. + if (!creatives || creatives.length === 0) return houseFill(format); type CampaignJoin = { id: string; @@ -125,7 +127,8 @@ export async function serveAd( return spentToday + CPC_CENTS <= c.daily_budget_cents; }, ); - if (eligible.length === 0) return null; + // Every candidate is out of budget → fall back to the house ad. + if (eligible.length === 0) return houseFill(format); const pick = eligible[Math.floor(Math.random() * eligible.length)]; const campaign = oneCampaign(pick.ad_campaigns); diff --git a/public/ads/house/promo-rect.webp b/public/ads/house/promo-rect.webp new file mode 100644 index 0000000000000000000000000000000000000000..b874bbf803a12d63056b0b33d0e7e0c9b5fff0c2 GIT binary patch literal 7788 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 literal 0 HcmV?d00001