ваше сообщение коммита

This commit is contained in:
2026-04-22 13:41:56 +03:00
parent 1bf6b13246
commit ece395a60f
11 changed files with 444 additions and 164 deletions

View File

@@ -7,13 +7,14 @@
<title>VC HB3 Accelerator — венчурный фонд и платформа DLE</title>
<meta name="description" content="Финтех-стартап VC HB3 Accelerator: венчурный фонд и поставщик ПО. Платформа DLE для бизнеса, акселератор, песочница. Для инвесторов, предпринимателей, регуляторов, подрядчиков." />
<meta name="keywords" content="VC HB3 Accelerator, венчурный фонд, финтех, DLE, Digital Legal Entity, акселератор, песочница, для инвесторов, для предпринимателей, для регуляторов" />
<link rel="canonical" href="https://hb3-accelerator.com/" />
<meta name="robots" content="index, follow" />
<link rel="canonical" href="/" />
<!-- Open Graph — информация о компании VC HB3 Accelerator -->
<meta property="og:type" content="website" />
<meta property="og:title" content="VC HB3 Accelerator — венчурный фонд и платформа DLE" />
<meta property="og:description" content="Добро пожаловать в VC HB3 Accelerator. Финтех-стартап — венчурный фонд и поставщик ПО. Разрабатываем платформу DLE, помогаем бизнесу настроить и протестировать её в песочнице. Инвестируем в компании участников акселератора." />
<meta property="og:url" content="https://hb3-accelerator.com/" />
<meta property="og:image" content="https://hb3-accelerator.com/dle-logo.png" />
<meta property="og:url" content="/" />
<meta property="og:image" content="/dle-logo.png" />
<meta property="og:locale" content="ru_RU" />
<meta property="og:site_name" content="VC HB3 Accelerator" />
<!-- Twitter Card -->

View File

@@ -67,6 +67,20 @@ http {
add_header X-XSS-Protection "1; mode=block" always;
}
location /blog {
if ($arg_page != "") {
return 301 /blog;
}
try_files $uri $uri/ /index.html;
}
location = /content/published {
if ($arg_page != "") {
return 301 /content/published;
}
try_files $uri $uri/ /index.html;
}
# Certbot webroot для автоматического получения SSL сертификатов
location /.well-known/acme-challenge/ {
root /var/www/certbot;
@@ -120,6 +134,21 @@ http {
proxy_set_header X-Forwarded-Port $server_port;
}
# Gitea служебный раздел: запрещаем индексацию
location ^~ /gitea/ {
proxy_pass http://dapp-gitea:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
add_header X-Robots-Tag "noindex, nofollow, noarchive, nosnippet, noimageindex" always;
proxy_redirect off;
proxy_http_version 1.1;
proxy_connect_timeout 120s;
proxy_send_timeout 1800s;
proxy_read_timeout 1800s;
}
# Скрытие информации о сервере
server_tokens off;
}

View File

@@ -221,6 +221,7 @@ http {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
add_header X-Robots-Tag "noindex, nofollow, noarchive, nosnippet, noimageindex" always;
proxy_redirect off;
proxy_http_version 1.1;
proxy_connect_timeout 120s;
@@ -253,6 +254,9 @@ http {
# Pre-rendered blog pages (SEO optimization)
location /blog {
if ($arg_page != "") {
return 301 /blog;
}
root /usr/share/nginx/html;
try_files $uri $uri.html /blog/index.html /index.html;
@@ -262,6 +266,13 @@ http {
add_header X-Frame-Options "SAMEORIGIN" always;
}
location = /content/published {
if ($arg_page != "") {
return 301 /content/published;
}
try_files $uri $uri/ /index.html;
}
# Статические файлы
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;

View File

@@ -180,7 +180,7 @@
</template>
<script setup>
import { ref, computed, onMounted, watch, nextTick } from 'vue';
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { marked } from 'marked';
import DOMPurify from 'dompurify';
@@ -220,6 +220,23 @@ const relatedArticles = ref([]);
// Определяем, это страница блога
const isBlogPage = computed(() => route.path.startsWith('/blog'));
const isPublishedPage = computed(() => route.path.startsWith('/content/published'));
const shouldManageNoindex = computed(() => isBlogPage.value || isPublishedPage.value);
function setRobotsMeta(content = 'index, follow') {
let robotsMeta = document.querySelector('meta[name="robots"]');
if (!robotsMeta) {
robotsMeta = document.createElement('meta');
robotsMeta.setAttribute('name', 'robots');
document.head.appendChild(robotsMeta);
}
// Не выставляем noindex вне документных маршрутов, чтобы не блокировать индексацию главной.
if (content.startsWith('noindex') && !shouldManageNoindex.value) {
robotsMeta.setAttribute('content', 'index, follow');
return;
}
robotsMeta.setAttribute('content', content);
}
// Установка мета-тегов для SEO
function updateMetaTags(pageData) {
@@ -240,22 +257,21 @@ function updateMetaTags(pageData) {
const description = seoData?.description || pageData.summary || '';
const keywords = seoData?.keywords || '';
// Определяем canonical URL в зависимости от текущего маршрута и наличия slug
// Определяем canonical URL только по slug (без fallback на ?page=, чтобы исключить дубли)
const currentPath = window.location.pathname;
let canonicalUrl;
if (currentPath.startsWith('/blog')) {
// Используем slug если есть, иначе fallback на query параметр
if (pageData.slug && typeof pageData.slug === 'string' && pageData.slug.trim() !== '') {
canonicalUrl = `${window.location.origin}/blog/${encodeURIComponent(pageData.slug)}`;
} else if (pageData.id) {
canonicalUrl = `${window.location.origin}/blog?page=${pageData.id}`;
} else {
canonicalUrl = `${window.location.origin}/blog`;
if (!pageData.slug || typeof pageData.slug !== 'string' || pageData.slug.trim() === '') {
setRobotsMeta('noindex, follow');
return;
}
canonicalUrl = `${window.location.origin}/blog/${encodeURIComponent(pageData.slug.trim())}`;
} else {
canonicalUrl = pageData.id
? `${window.location.origin}/content/published?page=${pageData.id}`
: `${window.location.origin}/content/published`;
if (!pageData.slug || typeof pageData.slug !== 'string' || pageData.slug.trim() === '') {
setRobotsMeta('noindex, follow');
return;
}
canonicalUrl = `${window.location.origin}/content/published/${encodeURIComponent(pageData.slug.trim())}`;
}
// Обновляем title
@@ -297,7 +313,7 @@ function updateMetaTags(pageData) {
updateOrCreateMeta('og:url', canonicalUrl, 'property');
// Robots meta
updateOrCreateMeta('robots', 'index, follow');
setRobotsMeta('index, follow');
// Добавляем JSON-LD разметку для статьи
addArticleJsonLd(pageData, canonicalUrl);
@@ -487,11 +503,19 @@ async function loadPage() {
if (error.response?.data && error.response.status !== 404) {
console.warn('[DocsContent] Пытаемся использовать данные из error.response.data');
page.value = error.response.data;
if (!page.value || !page.value.id) {
setRobotsMeta('noindex, follow');
}
} else {
page.value = null;
page.value = null;
// Для любого error-state показываем noindex, иначе Google получает soft-404.
setRobotsMeta('noindex, follow');
}
} finally {
isLoading.value = false;
if (!page.value) {
setRobotsMeta('noindex, follow');
}
console.log('[DocsContent] loadPage завершен:', {
hasPage: !!page.value,
isLoading: isLoading.value,
@@ -831,6 +855,11 @@ onMounted(() => {
}
setupVideoErrorHandlers();
});
onUnmounted(() => {
// В SPA noindex может "залипать" между маршрутами, поэтому сбрасываем его при уходе со страницы документа.
setRobotsMeta('index, follow');
});
</script>
<style scoped>

View File

@@ -243,11 +243,12 @@ function addBlogJsonLd() {
'name': 'Блог',
'description': 'Публикации и статьи',
'url': `${window.location.origin}/blog`,
'blogPost': pages.value.slice(0, 10).map(page => {
const url = (page.slug && typeof page.slug === 'string' && page.slug.trim() !== '')
? `${window.location.origin}/blog/${encodeURIComponent(page.slug)}`
: (page.id ? `${window.location.origin}/blog?page=${page.id}` : `${window.location.origin}/blog`);
'blogPost': pages.value
.filter(page => page.slug && typeof page.slug === 'string' && page.slug.trim() !== '')
.slice(0, 10)
.map(page => {
const url = `${window.location.origin}/blog/${encodeURIComponent(page.slug.trim())}`;
return {
'@type': 'BlogPosting',
'headline': page.title || '',

View File

@@ -266,9 +266,21 @@ function updatePageMetaTags() {
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}`;
if (!page.value.slug || typeof page.value.slug !== 'string' || page.value.slug.trim() === '') {
// Без slug страницу не индексируем, чтобы не плодить дубли по ?page=
const robotsMeta = document.querySelector('meta[name="robots"]');
if (robotsMeta) {
robotsMeta.setAttribute('content', 'noindex, follow');
} else {
const meta = document.createElement('meta');
meta.setAttribute('name', 'robots');
meta.setAttribute('content', 'noindex, follow');
document.head.appendChild(meta);
}
return;
}
const pageUrl = `${window.location.origin}/content/published/${encodeURIComponent(page.value.slug.trim())}`;
// Обновляем title
document.title = title;