P0-2 + Phase A: drop Adsanity iframes, fix SEO baseline, noindex while staging

P0-2 (Adsanity iframe path removed):
- AdImage.tsx: deleted the iframe branch and the ADSANITY_CAMPAIGN_MAP
  fallback. AdImage now renders local images only; banners without a
  src render nothing.
- ads.ts: removed Ad.adsanity_id from the interface. Pruned AJA, Zixi,
  Lectrosonics, Opengear, Studio Hero, Magewell 300x250 and Blackmagic
  300x600 — they relied on ads.broadcastbeat.com which does not resolve.
  Surviving live banners: LiveU 728x90, LiveU PAYG 300x250, Telycam 300x250.
- SidebarAdStack: key no longer references the removed field.
- No banner_analytics rows reference ads.broadcastbeat.com.

Phase A (SEO baseline):
- robots.ts: fallback base URL now broadcastbeat.com (was rocket staging URL).
- sitemap.ts: fallback base URL now broadcastbeat.com (was bb-staging).
- news/[slug]/page.tsx and articles/[slug]/page.tsx: same fallback fix.
  JSON-LD now uses ISO 8601 for datePublished, absolute image URL via
  SITE_URL prefix, and publisher.logo points at /assets/images/logo.png
  instead of the 404-ing /legacy/site/broadcastbeat-logo.png.
- layout.tsx: removed the /en /es /pt /fr /de /zh /ja /ar /hi hreflang
  alternates - those routes 404 today; Phase D will reinstate. Replaced
  rocket.new Organization schema URL with broadcastbeat.com. Set
  robots index/follow=false until Phase B-D land and Rich Results passes.

Featured-image fallback hardening:
- legacy-source: rowToArticle now treats BB_Gray, no_image and placeholder
  URLs as null and falls back to the new placeholder so dead images do not
  leak through to the page.
- public/assets/images/article-placeholder.svg: new clean dark gradient
  with BroadcastBeat wordmark, NOT no_image.png style.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Code
2026-05-14 16:53:42 +00:00
parent 8be2ccbff2
commit 02b69f2252
10 changed files with 114 additions and 108 deletions

View File

@@ -16,7 +16,19 @@ interface PageProps {
}
const SITE_URL =
process.env.NEXT_PUBLIC_SITE_URL || "https://bb-staging.onsethost.com";
process.env.NEXT_PUBLIC_SITE_URL || "https://broadcastbeat.com";
function toIso(d: string | null | undefined): string {
if (!d) return new Date().toISOString();
const parsed = new Date(d);
return isNaN(parsed.getTime()) ? new Date().toISOString() : parsed.toISOString();
}
function absoluteImage(src: string | undefined | null): string {
if (!src) return `${SITE_URL}/assets/images/article-placeholder.jpg`;
if (/^https?:\/\//i.test(src)) return src;
return `${SITE_URL}${src.startsWith("/") ? "" : "/"}${src}`;
}
async function loadArticle(slug: string): Promise<{
article: Article | null;
@@ -77,15 +89,16 @@ export default async function ArticlePage({ params }: PageProps) {
"@type": "Article",
headline: article.title,
description: article.excerpt,
image: article.image,
image: [absoluteImage(article.image)],
author: { "@type": "Person", name: article.author },
publisher: {
"@type": "Organization",
name: "BroadcastBeat",
logo: { "@type": "ImageObject", url: `${SITE_URL}/legacy/site/broadcastbeat-logo.png` },
logo: { "@type": "ImageObject", url: `${SITE_URL}/assets/images/logo.png` },
},
datePublished: article.date,
datePublished: toIso(article.date),
dateModified: new Date().toISOString(),
mainEntityOfPage: { "@type": "WebPage", "@id": `${SITE_URL}/articles/${article.slug}` },
};
return (

View File

@@ -20,18 +20,9 @@ export const metadata: Metadata = {
},
alternates: {
canonical: '/',
languages: {
'en': '/en',
'es': '/es',
'pt': '/pt',
'fr': '/fr',
'de': '/de',
'zh': '/zh',
'ja': '/ja',
'ar': '/ar',
'hi': '/hi',
'x-default': '/en',
},
// hreflang entries removed — we don't currently serve translated content,
// and pointing Google at /en, /es, /pt, etc. (which 404) was poisoning
// the SEO signal. Re-add when the i18n routes actually exist.
},
openGraph: {
type: 'website',
@@ -59,11 +50,14 @@ export const metadata: Metadata = {
images: ['/assets/images/og-image.png']
},
robots: {
index: true,
follow: true,
// noindex while Phase B (PR rewrite) and Phase D (i18n routes) land.
// Flip both to true once Google Rich Results Test passes on a sample
// NewsArticle and the news sitemap is wired up (Phase C).
index: false,
follow: false,
googleBot: {
index: true,
follow: true,
index: false,
follow: false,
'max-video-preview': -1,
'max-image-preview': 'large',
'max-snippet': -1
@@ -78,17 +72,7 @@ export default function RootLayout({
return (
<html lang="en">
<head>
{/* hreflang tags for multilingual SEO — Section 18G */}
<link rel="alternate" hrefLang="en" href={`${process.env.NEXT_PUBLIC_SITE_URL || ''}/en`} />
<link rel="alternate" hrefLang="es" href={`${process.env.NEXT_PUBLIC_SITE_URL || ''}/es`} />
<link rel="alternate" hrefLang="pt" href={`${process.env.NEXT_PUBLIC_SITE_URL || ''}/pt`} />
<link rel="alternate" hrefLang="fr" href={`${process.env.NEXT_PUBLIC_SITE_URL || ''}/fr`} />
<link rel="alternate" hrefLang="de" href={`${process.env.NEXT_PUBLIC_SITE_URL || ''}/de`} />
<link rel="alternate" hrefLang="zh" href={`${process.env.NEXT_PUBLIC_SITE_URL || ''}/zh`} />
<link rel="alternate" hrefLang="ja" href={`${process.env.NEXT_PUBLIC_SITE_URL || ''}/ja`} />
<link rel="alternate" hrefLang="ar" href={`${process.env.NEXT_PUBLIC_SITE_URL || ''}/ar`} />
<link rel="alternate" hrefLang="hi" href={`${process.env.NEXT_PUBLIC_SITE_URL || ''}/hi`} />
<link rel="alternate" hrefLang="x-default" href={`${process.env.NEXT_PUBLIC_SITE_URL || ''}/en`} />
{/* hreflang entries removed pending real i18n routes (Phase D). */}
<script
type="application/ld+json"
@@ -97,7 +81,7 @@ export default function RootLayout({
'@context': 'https://schema.org',
'@type': 'Organization',
name: 'BroadcastBeat',
url: 'https://broadcastb5322.builtwithrocket.new',
url: 'https://broadcastbeat.com',
logo: "https://img.rocket.new/generatedImages/rocket_gen_img_1ab0c6b82-1768715893826.png",
description: 'The digital platform for broadcast engineering professionals. Breaking news, deep-dive features, and industry spotlights from NAB to IBC and beyond.',
sameAs: [
@@ -109,7 +93,7 @@ export default function RootLayout({
contactPoint: {
'@type': 'ContactPoint',
contactType: 'Editorial',
url: 'https://broadcastb5322.builtwithrocket.new'
url: 'https://broadcastbeat.com'
}
})
}} />

View File

@@ -18,7 +18,21 @@ interface PageProps {
}
const SITE_URL =
process.env.NEXT_PUBLIC_SITE_URL || "https://bb-staging.onsethost.com";
process.env.NEXT_PUBLIC_SITE_URL || "https://broadcastbeat.com";
/** Convert a free-form date (e.g. "April 9, 2026") to ISO 8601 for JSON-LD. */
function toIso(d: string | null | undefined): string {
if (!d) return new Date().toISOString();
const parsed = new Date(d);
return isNaN(parsed.getTime()) ? new Date().toISOString() : parsed.toISOString();
}
/** Make an image path absolute against SITE_URL for Google News compliance. */
function absoluteImage(src: string | undefined | null): string {
if (!src) return `${SITE_URL}/assets/images/article-placeholder.jpg`;
if (/^https?:\/\//i.test(src)) return src;
return `${SITE_URL}${src.startsWith("/") ? "" : "/"}${src}`;
}
async function loadArticle(slug: string): Promise<{
article: Article | null;
@@ -80,14 +94,14 @@ export default async function NewsArticlePage({ params }: PageProps) {
"@type": "NewsArticle",
headline: article.title,
description: article.excerpt,
image: article.image,
image: [absoluteImage(article.image)],
author: { "@type": "Person", name: article.author },
publisher: {
"@type": "Organization",
name: "BroadcastBeat",
logo: { "@type": "ImageObject", url: `${SITE_URL}/legacy/site/broadcastbeat-logo.png` },
logo: { "@type": "ImageObject", url: `${SITE_URL}/assets/images/logo.png` },
},
datePublished: article.date,
datePublished: toIso(article.date),
dateModified: new Date().toISOString(),
mainEntityOfPage: { "@type": "WebPage", "@id": `${SITE_URL}/news/${article.slug}` },
};

View File

@@ -1,7 +1,7 @@
import { MetadataRoute } from 'next';
export default function robots(): MetadataRoute.Robots {
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://broadcastb5322.builtwithrocket.new';
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://broadcastbeat.com';
return {
rules: [

View File

@@ -1,7 +1,7 @@
import { MetadataRoute } from 'next';
import { getLegacyRecentSitemapEntries } from '@/lib/articles/legacy-source';
const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL || 'https://bb-staging.onsethost.com';
const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL || 'https://broadcastbeat.com';
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
// Static pages