mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 15:13:07 +00:00
O fallback de imagem OG (própria → global `metadata` → null, sempre absoluto
no deployment origin) deixa de estar copiado em 6–7 páginas públicas e ganha
o dono server-only `src/utilities/ogImageReads.ts` (`resolveOgImage(configured)
→ { url, media }`), com `resolveShareLinkOgImageUrl` delegando (sem twin) e os
8 consumidores migrados — 2 do S27, jingles, corte, abaixo-assinado ×2, artigo
e layout. `seo.ts` segue puro; sem migration, sem Consent/access, Impeccable A.
- unit novo do resolver (8 casos) + pin do módulo em codebaseConventions; int
do shareLink verde sem tocar asserção; e2e frontendConteudos/frontendJingles/
campaignSpeechCut/frontendShareLink/frontend verdes, com asserts novos de
og:image (helper `metaContent` extraído do spec do share-link)
- dono novo entra no E2E_AFFECTED_MANIFEST nas 4 entradas de OG públicas
- deltas intencionais registrados: deployment origin no lugar do canônico em
corte/abaixo-assinado/artigo e OG absoluto no layout
76 lines
3.3 KiB
TypeScript
76 lines
3.3 KiB
TypeScript
import 'server-only'
|
|
|
|
import type { Media } from '@/payload-types'
|
|
import { getCachedDocumentById } from '@/utilities/documentReads'
|
|
import { getCachedGlobal } from '@/utilities/globalReads'
|
|
import { resolveDeploymentOrigin, resolveSiteMetadata, toAbsoluteUrl } from '@/utilities/seo'
|
|
|
|
/**
|
|
* A page's own OG image: an upload (`Media`), a bare upload id (depth-0 reads
|
|
* and poisoned cache entries) or an already-resolved URL string.
|
|
*/
|
|
export type OgImageSource = Media | number | string | null | undefined
|
|
|
|
export type ResolvedOgImage = {
|
|
/**
|
|
* Absolute URL — app paths resolved on the deployment origin; an
|
|
* already-absolute URL (e.g. a YouTube thumbnail) passes through. `null`
|
|
* when nothing resolved.
|
|
*/
|
|
url: string | null
|
|
/** The chosen `Media` when the source is an upload; `null` for URL sources. */
|
|
media: Media | null
|
|
}
|
|
|
|
const mediaOf = (value: OgImageSource): Media | null =>
|
|
typeof value === 'object' && value !== null ? value : null
|
|
|
|
const resolveMediaSource = async (value: OgImageSource): Promise<Media | null> =>
|
|
typeof value === 'number' ? getCachedDocumentById('media', String(value))() : mediaOf(value)
|
|
|
|
/**
|
|
* Absolute URL on the deployment origin. An already-absolute URL (e.g. a
|
|
* YouTube thumbnail) passes through; a relative path needs an origin — never a
|
|
* relative URL the WhatsApp crawler cannot resolve.
|
|
*/
|
|
const absolutize = (url: string | null | undefined, origin: string | null): string | null => {
|
|
const trimmed = url?.trim()
|
|
if (!trimmed) return null
|
|
if (origin) return toAbsoluteUrl(trimmed, origin)
|
|
return /^https?:\/\//i.test(trimmed) ? trimmed : null
|
|
}
|
|
|
|
/**
|
|
* Single owner of the public frontend's OG image fallback: the page's own
|
|
* image when it has a usable URL, otherwise the site default image of the
|
|
* `metadata` global (resolving the id when the read came back at depth 0).
|
|
*
|
|
* Media lives behind this app's `/api/media/file/…` proxy, so asset URLs are
|
|
* built on the deployment origin (`NEXT_PUBLIC_SITE_URL`), never on the
|
|
* `metadata` global `URL`: the global can be a canonical domain served by
|
|
* another platform (today the legacy WordPress), which 404s the proxy path and
|
|
* makes WhatsApp drop the thumbnail. The global `siteUrl` is the fallback when
|
|
* the env is missing.
|
|
*
|
|
* `media` is the resolved upload when the chosen source is one, so call sites
|
|
* that emit width/height/alt/mimeType keep the full document; URL-only sources
|
|
* return `media: null`. Canonical and JSON-LD `url` are NOT this module's
|
|
* business — they keep the global-first chain of `resolveSiteMetadata`.
|
|
*/
|
|
export const resolveOgImage = async (configured: OgImageSource): Promise<ResolvedOgImage> => {
|
|
const globalMetadata = await getCachedGlobal('metadata')()
|
|
const { siteUrl } = resolveSiteMetadata(globalMetadata)
|
|
const deploymentOrigin = resolveDeploymentOrigin(siteUrl)
|
|
|
|
const configuredMedia = await resolveMediaSource(configured)
|
|
const configuredUrl = absolutize(
|
|
typeof configured === 'string' ? configured : configuredMedia?.url,
|
|
deploymentOrigin,
|
|
)
|
|
if (configuredUrl) return { url: configuredUrl, media: configuredMedia }
|
|
|
|
const fallbackMedia = await resolveMediaSource(globalMetadata.image)
|
|
const fallbackUrl = absolutize(fallbackMedia?.url, deploymentOrigin)
|
|
|
|
return { url: fallbackUrl, media: fallbackUrl ? fallbackMedia : null }
|
|
}
|