product

AisleAsk — public storefront SEO deals page

Ask (2026-07-24): take a photo of the front of the store (upload / camera — Yelp/Google auto-fetch later), run it through a painterly artistic filter, and use it as the hero of a public, indexable per-store deals page — an SEO surface for "[store] deals / weekly ad near me".

Why this is a good wedge

  • Every crowd-mapped store becomes a landing page ranking for its own name + "deals". The deals data already exists (aisleask_deals, crowd-scanned).
  • The painterly filter makes the hero recognizably their storefront but stylized — distinctive, and it sidesteps re-publishing a third party's exact copyrighted photo verbatim.
  • Public pages feed people back into AisleAsk (shop-the-deals + aisle routing).

Photo source (two ToS-clean paths)

  1. Upload / camera (generateStorefront) — owner supplies the storefront photo; gpt-image-1 image-EDIT repaints it painterly. Compliant because it's the owner's own image.
  2. ✨ Generate (generateStorefrontHero) — fully generative painterly exterior from the store's name/type via gpt-image-1, no source photo. The one-tap "auto" path: no upload, no Google imagery.

Why NOT Google Places auto-fetch (decided 2026-07-25): we scoped pulling the storefront photo from Google Places (QS could serve it — they have the pipeline) and rejected it on Google's Place Photos ToS: those terms require showing the photo's attributions and bar caching + derivative works, so painterly-transforming a Places photo and re-hosting it as our own SEO hero doesn't comply. A generative hero sidesteps this entirely (no Google input). The Places direction + QS's cost/ ToS analysis is preserved in crosstalk/contracts/aisleask-places-storefront.md (status: declined-on-ToS). Yelp has the same derivative/attribution constraints.

Data model (migration 571)

Additive on aisleask_stores:

  • storefront_image_url text — the painterly hero (gpt-image-1 image-EDIT of the uploaded photo).
  • public_slug text (unique, partial index) — the /store/<slug> URL key, derived from the store name, deduped with a numeric suffix.
  • public_deals_enabled boolean NOT NULL DEFAULT false — owner opt-in; the page 404s unless true. Generating a storefront flips it on.

Backend

  • generateStorefront(userId, storeId, dataUri) — decodes the uploaded photo, runs gpt-image-1 image-edit with a painterly-repaint prompt keyed to the store name (keeps the real building shape/signage recognizable), uploads to the aisleask Storage bucket (storefronts/<storeId>.png), mints a public_slug, sets storefront_image_url, and turns the page on. Mirrors season-media.ts images.edit + toFile. POST /:storeId/storefront { image }.
  • getPublicStore(slug)no-auth service-role read; returns the store's public fields + active deals, or null unless public_deals_enabled. GET /public/store/:slug (registered before the /:storeId/* routes).
  • public_deals_enabled flows through updateStore / sanitizeStorePatch — the owner can toggle the page off (checkbox on the dashboard).

Frontend

  • Public page apps/frontend/src/app/store/[slug]/page.tsx — server component, generateMetadata (OG image = painterly storefront), JSON-LD GroceryStore + Offers for rich results, painterly hero, deals grid, "Open AisleAsk" CTA. Canonical/OG on www.hivejournal.com. ISR revalidate: 600.
  • Dashboard (Map tab): a "🎨 Public deals page (SEO)" block — upload storefront photo → filter → preview + a "View public page ↗" link + a Public on/off toggle.

Version history (migration 572)

Every generated/uploaded hero is kept — a distinct Storage object under storefronts/<storeId>/<stamp>.png. aisleask_stores.storefront_image_history (text[], newest-first, capped 12) holds all versions; storefront_image_url stays the CURRENT hero the public page shows. The dashboard renders a thumbnail row; clicking an older one repoints the current via POST /:storeId/storefront/select { url } (validates membership — no regeneration). Apply migration 572.

Later

  • QuickSites stands up the Places proxy (contract above) → HJ flips one env var.
  • Per-store OG card composited with the deal count.
  • City/chain index pages ("QFC deals near Renton") aggregating store pages.
AISLEASK STORE SEO — Docs | HiveJournal