From ed5677bcfb4828099f4e9c2ad1f2e27cf8832669 Mon Sep 17 00:00:00 2001 From: Zotov Ivan Yuryevich Date: Sat, 15 Aug 2026 17:58:58 +0300 Subject: [PATCH] feat: add og tags --- src/env.d.ts | 2 + src/graphql-documents/privacy-policy.gql.ts | 1 + src/graphql-documents/terms-of-use.gql.ts | 1 + src/graphql/graphql.ts | 6 +- src/layouts/DocumentLayout.astro | 10 ++- src/layouts/Layout.astro | 68 ++++++++++++++++++++- src/pages/404.astro | 7 ++- src/pages/500.astro | 7 ++- src/pages/[slug].astro | 3 + src/pages/coming-soon.astro | 15 +++++ src/pages/index.astro | 2 +- src/pages/privacy-policy.astro | 1 + src/pages/terms-of-use.astro | 1 + 13 files changed, 114 insertions(+), 10 deletions(-) diff --git a/src/env.d.ts b/src/env.d.ts index b5df4da..fad972a 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -4,6 +4,8 @@ interface ImportMetaEnv { readonly PUBLIC_API_URL: string readonly PUBLIC_GRAPHQL_URL: string readonly PUBLIC_UPLOADS_BASE_URL: string + /** Public origin of the site, e.g. https://softclick.ru — used for canonical/og:url. */ + readonly PUBLIC_SITE_URL?: string readonly BITRIX24_WEBHOOK_URL?: string } diff --git a/src/graphql-documents/privacy-policy.gql.ts b/src/graphql-documents/privacy-policy.gql.ts index 429ac1f..e04507f 100644 --- a/src/graphql-documents/privacy-policy.gql.ts +++ b/src/graphql-documents/privacy-policy.gql.ts @@ -4,6 +4,7 @@ export const privacyPolicyDocument = gql` query PrivacyPolicy { siteConfig { title + description privacyPolicy } } diff --git a/src/graphql-documents/terms-of-use.gql.ts b/src/graphql-documents/terms-of-use.gql.ts index eb9b48e..cd93770 100644 --- a/src/graphql-documents/terms-of-use.gql.ts +++ b/src/graphql-documents/terms-of-use.gql.ts @@ -4,6 +4,7 @@ export const termsOfUseDocument = gql` query TermsOfUse { siteConfig { title + description termsOfUse } } diff --git a/src/graphql/graphql.ts b/src/graphql/graphql.ts index c1260cf..82ae9b6 100644 --- a/src/graphql/graphql.ts +++ b/src/graphql/graphql.ts @@ -1632,12 +1632,12 @@ export type SiteConfigQuery = { __typename?: 'Query', siteConfig?: { __typename? export type PrivacyPolicyQueryVariables = Exact<{ [key: string]: never; }>; -export type PrivacyPolicyQuery = { __typename?: 'Query', siteConfig?: { __typename?: 'SiteConfig', title: string, privacyPolicy?: string | null } | null }; +export type PrivacyPolicyQuery = { __typename?: 'Query', siteConfig?: { __typename?: 'SiteConfig', title: string, description?: string | null, privacyPolicy?: string | null } | null }; export type TermsOfUseQueryVariables = Exact<{ [key: string]: never; }>; -export type TermsOfUseQuery = { __typename?: 'Query', siteConfig?: { __typename?: 'SiteConfig', title: string, termsOfUse?: string | null } | null }; +export type TermsOfUseQuery = { __typename?: 'Query', siteConfig?: { __typename?: 'SiteConfig', title: string, description?: string | null, termsOfUse?: string | null } | null }; export class TypedDocumentString extends String @@ -1806,6 +1806,7 @@ export const PrivacyPolicyDocument = new TypedDocumentString(` query PrivacyPolicy { siteConfig { title + description privacyPolicy } } @@ -1814,6 +1815,7 @@ export const TermsOfUseDocument = new TypedDocumentString(` query TermsOfUse { siteConfig { title + description termsOfUse } } diff --git a/src/layouts/DocumentLayout.astro b/src/layouts/DocumentLayout.astro index 33b6ebe..b4de174 100644 --- a/src/layouts/DocumentLayout.astro +++ b/src/layouts/DocumentLayout.astro @@ -4,14 +4,20 @@ import '../styles/globals.css' interface Props { siteTitle: string + siteDescription?: string pageTitle: string contentHtml: string } -const { siteTitle, pageTitle, contentHtml } = Astro.props +const { siteTitle, siteDescription = '', pageTitle, contentHtml } = Astro.props --- - +
diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 6c71540..b6f8708 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -2,20 +2,82 @@ import ClientRouter from 'astro/components/ClientRouter.astro' interface Props { + /** Full / og:title. Comes from Strapi (siteConfig.title, category.title). */ title?: string + /** meta description / og:description. Comes from Strapi. */ description?: string + /** og:site_name — the brand, without the per-page part. */ + siteName?: string + /** og:type: 'website' for the home page, 'article' for content pages. */ + type?: 'website' | 'article' + /** Absolute or site-root-relative image for og:image / twitter:image. */ + image?: string + /** Pixel size of `image` — crawlers use it to lay the preview out before download. */ + imageWidth?: number + imageHeight?: number + /** Override the canonical path (defaults to the current URL without query). */ + canonicalPath?: string + /** Keep the page out of search indexes (error pages, gated pages). */ + noindex?: boolean } -const { title = '', description = '' } = Astro.props +const { + title = '', + description = '', + siteName = '', + type = 'website', + image = '/logo.jpg', + imageWidth = 1024, // matches public/logo.jpg + imageHeight = 1024, + canonicalPath, + noindex = false, +} = Astro.props + +// Absolute URLs are required by Open Graph — relative ones are ignored by most +// crawlers and social scrapers. PUBLIC_SITE_URL pins the public origin; behind a +// reverse proxy Astro.url alone can resolve to the internal container host. +// Prefer process.env so the domain can be set at deploy/runtime (Docker), with +// import.meta.env as the `astro dev` fallback (see src/middleware.ts). +const siteOrigin = + process.env.PUBLIC_SITE_URL || import.meta.env.PUBLIC_SITE_URL || Astro.url.origin +const canonicalURL = new URL(canonicalPath ?? Astro.url.pathname, siteOrigin) +const imageURL = new URL(image, siteOrigin) --- <html lang="ru"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> - <meta name="description" content={description} /> - <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <title>{title} + + + { + noindex ? ( + + ) : ( + + ) + } + + + + + + + {siteName && } + + + + + + + + + + + + +