f3b89ca9d7
One presentational SeoHead renders the full OG/Twitter/JSON-LD head surface at prerender via typed schema.org builders. Per-medium release schema, config-sourced canonicals, 404 noindex. Zero CMS change.
52 lines
2.8 KiB
C#
52 lines
2.8 KiB
C#
namespace DeepDrftPublic.Client.Common;
|
||
|
||
/// <summary>
|
||
/// Site-wide SEO defaults (Phase 22). These are non-secret brand constants — a single canonical origin,
|
||
/// the site name/suffix, the fallback share image, the social links — sourced once and injected into
|
||
/// <c>SeoHead</c> so no page re-declares them. Registered as a singleton in
|
||
/// <see cref="Startup.ConfigureDomainServices"/>, which runs in <b>both</b> the server prerender and the
|
||
/// WASM passes, so both passes resolve identical values (the double-render-identity requirement, §5/AC6).
|
||
///
|
||
/// <para>
|
||
/// <see cref="BaseUrl"/> is the load-bearing field: absolute canonical / <c>og:url</c> / <c>og:image</c>
|
||
/// origins all come from here, never from a browser API — there is no <c>window.location</c> during
|
||
/// server prerender, and the request host is unreliable behind the nginx reverse proxy (§5, OQ1).
|
||
/// </para>
|
||
/// </summary>
|
||
public sealed record SeoOptions
|
||
{
|
||
/// <summary>Canonical production origin, no trailing slash. Absolute URLs are this + a resolved path (OQ1).</summary>
|
||
public string BaseUrl { get; init; } = "https://deepdrft.com";
|
||
|
||
/// <summary>The brand name used in <c>og:site_name</c>, <c>application-name</c>, and the JSON-LD MusicGroup.</summary>
|
||
public string SiteName { get; init; } = "Deep DRFT";
|
||
|
||
/// <summary>Appended to a page's bare title as <c>"{Title} · {TitleSuffix}"</c>. Resolves the prior suffix inconsistency (OQ4).</summary>
|
||
public string TitleSuffix { get; init; } = "Deep DRFT";
|
||
|
||
/// <summary>Fallback meta/OG description for pages that supply none.</summary>
|
||
public string DefaultDescription { get; init; } =
|
||
"Deep DRFT — an electronic music collective from Charleston, South Carolina. Studio cuts, live sessions, and DJ mixes.";
|
||
|
||
/// <summary>
|
||
/// Absolute or root-relative URL of the default 1200×630 share image used when a page has no cover (OQ2).
|
||
/// A placeholder path until the real asset is dropped in; swapping it is a one-value change.
|
||
/// </summary>
|
||
public string DefaultImageUrl { get; init; } = "/img/og-default.png";
|
||
|
||
/// <summary>OG locale. Optional surface tag.</summary>
|
||
public string Locale { get; init; } = "en_US";
|
||
|
||
/// <summary>The collective's primary genre, used in the MusicGroup JSON-LD node.</summary>
|
||
public string Genre { get; init; } = "Electronic";
|
||
|
||
/// <summary>Default robots directive; a page may override (e.g. the 404's <c>noindex,follow</c>).</summary>
|
||
public string DefaultRobots { get; init; } = "index,follow";
|
||
|
||
/// <summary>
|
||
/// Public social profile URLs for the MusicGroup <c>sameAs</c> array (OQ3). Instagram only —
|
||
/// no Twitter/X account exists, so no <c>twitter:site</c>/<c>twitter:creator</c> handle is emitted.
|
||
/// </summary>
|
||
public IReadOnlyList<string> SameAs { get; init; } = ["https://instagram.com/deepdrft.music"];
|
||
}
|