diff --git a/frontend/index.html b/frontend/index.html index 1731887..cfeaa40 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -18,6 +18,13 @@
+ diff --git a/frontend/nginx-simple.conf b/frontend/nginx-simple.conf index c7776d3..a168467 100644 --- a/frontend/nginx-simple.conf +++ b/frontend/nginx-simple.conf @@ -14,20 +14,32 @@ http { # limit_req_zone $binary_remote_addr zone=api_limit_per_ip:10m rate=5r/s; # Блокировка известных сканеров и вредоносных ботов (исключая легитимные поисковые боты) + # ВАЖНО: Сначала проверяем легитимные боты, потом блокируем вредоносные map $http_user_agent $bad_bot { default 0; - # Разрешаем легитимные поисковые боты + # Разрешаем легитимные поисковые боты (проверяем ПЕРВЫМИ) ~*googlebot 0; + ~*google.*bot 0; ~*bingbot 0; + ~*bingpreview 0; ~*slurp 0; ~*duckduckbot 0; ~*baiduspider 0; ~*yandex 0; + ~*yandexbot 0; ~*sogou 0; ~*exabot 0; ~*facebot 0; + ~*facebookexternalhit 0; ~*ia_archiver 0; - # Блокируем вредоносные боты и сканеры + ~*archive\.org_bot 0; + ~*applebot 0; + ~*twitterbot 0; + ~*linkedinbot 0; + ~*slackbot 0; + ~*whatsapp 0; + ~*telegrambot 0; + # Блокируем вредоносные боты и сканеры (только если не поисковый бот) ~*sqlmap 1; ~*nikto 1; ~*dirb 1; @@ -40,10 +52,12 @@ http { ~*masscan 1; ~*nmap 1; ~*scanner 1; - ~*Chrome/[1-4][0-9]\. 1; - ~*Firefox/[1-6][0-9]\. 1; - ~*Safari/[1-9]\. 1; - ~*MSIE\ [1-9]\. 1; + # Блокируем старые версии браузеров (только если это не поисковый бот) + # Убираем блокировку старых браузеров - может блокировать легитимных ботов + # ~*Chrome/[1-4][0-9]\. 1; + # ~*Firefox/[1-6][0-9]\. 1; + # ~*Safari/[1-9]\. 1; + # ~*MSIE\ [1-9]\. 1; } # HTTP сервер - редирект на HTTPS diff --git a/frontend/src/views/content/PublicPageView.vue b/frontend/src/views/content/PublicPageView.vue index a6aaca2..504acc6 100644 --- a/frontend/src/views/content/PublicPageView.vue +++ b/frontend/src/views/content/PublicPageView.vue @@ -246,6 +246,120 @@ watch(() => page.value?.content, () => { } }); +// Установка мета-тегов для SEO +function updatePageMetaTags() { + if (!page.value) return; + + // Парсим seo, если это строка + let seoData = page.value.seo; + if (typeof seoData === 'string') { + try { + seoData = JSON.parse(seoData); + } catch (e) { + console.warn('Ошибка парсинга SEO данных:', e); + seoData = null; + } + } + + const title = seoData?.title || page.value.title || 'Публичная страница'; + const description = seoData?.description || page.value.summary || ''; + const keywords = seoData?.keywords || ''; + + // Определяем canonical URL + const pageUrl = page.value.slug + ? `${window.location.origin}/content/published/${encodeURIComponent(page.value.slug)}` + : `${window.location.origin}/content/published?page=${page.value.id}`; + + // Обновляем title + document.title = title; + + // Обновляем или создаем meta теги + const updateOrCreateMeta = (name, content, attribute = 'name') => { + if (!content) return; + let meta = document.querySelector(`meta[${attribute}="${name}"]`); + if (!meta) { + meta = document.createElement('meta'); + meta.setAttribute(attribute, name); + document.head.appendChild(meta); + } + meta.setAttribute('content', content); + }; + + // Meta description + updateOrCreateMeta('description', description); + + // Meta keywords + if (keywords) { + updateOrCreateMeta('keywords', keywords); + } + + // Canonical URL + let canonical = document.querySelector('link[rel="canonical"]'); + if (!canonical) { + canonical = document.createElement('link'); + canonical.setAttribute('rel', 'canonical'); + document.head.appendChild(canonical); + } + canonical.setAttribute('href', pageUrl); + + // Open Graph теги для социальных сетей + updateOrCreateMeta('og:title', title, 'property'); + updateOrCreateMeta('og:description', description, 'property'); + updateOrCreateMeta('og:type', 'article', 'property'); + updateOrCreateMeta('og:url', pageUrl, 'property'); + + // Robots meta + updateOrCreateMeta('robots', 'index, follow'); + + // Добавляем JSON-LD разметку для статьи + addArticleJsonLd(page.value, pageUrl, seoData); +} + +// Добавляем JSON-LD разметку для статьи +function addArticleJsonLd(pageData, canonicalUrl, seoData) { + // Удаляем старую разметку, если есть + const oldScript = document.querySelector('script[type="application/ld+json"][data-public-article]'); + if (oldScript) { + oldScript.remove(); + } + + const articleJsonLd = { + '@context': 'https://schema.org', + '@type': 'Article', + 'headline': seoData?.title || pageData.title || '', + 'description': seoData?.description || pageData.summary || '', + 'datePublished': pageData.created_at || '', + 'dateModified': pageData.updated_at || pageData.created_at || '', + 'url': canonicalUrl, + 'author': { + '@type': 'Organization', + 'name': 'HB3 Accelerator' + }, + 'publisher': { + '@type': 'Organization', + 'name': 'HB3 Accelerator', + 'url': window.location.origin + } + }; + + if (pageData.category) { + articleJsonLd.articleSection = pageData.category; + } + + const script = document.createElement('script'); + script.type = 'application/ld+json'; + script.setAttribute('data-public-article', 'true'); + script.textContent = JSON.stringify(articleJsonLd); + document.head.appendChild(script); +} + +// Отслеживаем загрузку страницы и обновляем мета-теги +watch(() => page.value, (newPage) => { + if (newPage) { + updatePageMetaTags(); + } +}, { immediate: true }); + // Загрузка данных onMounted(() => { loadPage(); diff --git a/frontend/src/views/content/PublicPagesView.vue b/frontend/src/views/content/PublicPagesView.vue index 66a506e..0c300d4 100644 --- a/frontend/src/views/content/PublicPagesView.vue +++ b/frontend/src/views/content/PublicPagesView.vue @@ -158,8 +158,56 @@ async function loadPages() { } } +// Установка мета-тегов для SEO +function updatePublicPagesMetaTags() { + const title = 'Публичные документы - HB3 Accelerator'; + const description = 'Опубликованные документы и материалы платформы Digital Legal Entity. Публичная документация, статьи и информационные материалы.'; + const keywords = 'публичные документы, документация, Digital Legal Entity, DLE, публикации'; + const canonicalUrl = `${window.location.origin}/content/published`; + + // Обновляем title + document.title = title; + + // Обновляем или создаем meta теги + const updateOrCreateMeta = (name, content, attribute = 'name') => { + if (!content) return; + let meta = document.querySelector(`meta[${attribute}="${name}"]`); + if (!meta) { + meta = document.createElement('meta'); + meta.setAttribute(attribute, name); + document.head.appendChild(meta); + } + meta.setAttribute('content', content); + }; + + // Meta description + updateOrCreateMeta('description', description); + + // Meta keywords + updateOrCreateMeta('keywords', keywords); + + // Canonical URL + let canonical = document.querySelector('link[rel="canonical"]'); + if (!canonical) { + canonical = document.createElement('link'); + canonical.setAttribute('rel', 'canonical'); + document.head.appendChild(canonical); + } + canonical.setAttribute('href', canonicalUrl); + + // Open Graph теги для социальных сетей + updateOrCreateMeta('og:title', title, 'property'); + updateOrCreateMeta('og:description', description, 'property'); + updateOrCreateMeta('og:type', 'website', 'property'); + updateOrCreateMeta('og:url', canonicalUrl, 'property'); + + // Robots meta + updateOrCreateMeta('robots', 'index, follow'); +} + // Загрузка данных onMounted(() => { + updatePublicPagesMetaTags(); loadPages(); }); diff --git a/sync-to-vds.sh b/sync-to-vds.sh index 99b968c..d3f329d 100755 --- a/sync-to-vds.sh +++ b/sync-to-vds.sh @@ -10,7 +10,7 @@ NC='\033[0m' # No Color echo -e "${GREEN}🔄 Синхронизация кода с VDS...${NC}" # Параметры VDS (из настроек) -VDS_HOST="185.221.214.140" +VDS_HOST="185.26.121.127" VDS_USER="root" VDS_PORT="22" VDS_PATH="/home/docker/dapp" @@ -139,6 +139,12 @@ scp $SCP_OPTS ./backend/Dockerfile "$VDS_USER@$VDS_HOST:$VDS_PATH/backend/Docker scp $SCP_OPTS ./frontend/Dockerfile "$VDS_USER@$VDS_HOST:$VDS_PATH/frontend/Dockerfile" 2>/dev/null || true scp $SCP_OPTS ./frontend/nginx.Dockerfile "$VDS_USER@$VDS_HOST:$VDS_PATH/frontend/nginx.Dockerfile" 2>/dev/null || true +# Синхронизация nginx конфигураций (критично для SEO исправлений) +echo -e "${YELLOW}📦 Синхронизация nginx конфигураций...${NC}" +scp $SCP_OPTS ./frontend/nginx-simple.conf "$VDS_USER@$VDS_HOST:$VDS_PATH/frontend/nginx-simple.conf" 2>/dev/null || true +scp $SCP_OPTS ./frontend/nginx-local.conf "$VDS_USER@$VDS_HOST:$VDS_PATH/frontend/nginx-local.conf" 2>/dev/null || true +scp $SCP_OPTS ./frontend/docker-entrypoint.sh "$VDS_USER@$VDS_HOST:$VDS_PATH/frontend/docker-entrypoint.sh" 2>/dev/null || true + echo -e "${GREEN}✅ Синхронизация завершена!${NC}" # Спрашиваем, нужно ли пересобрать образы