diff --git a/public/llms.txt b/public/llms.txt index 9cca2ee..df4f050 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -13,6 +13,7 @@ - [Миры — 8 игровых вселенных](https://anotherreflections.ru/miry/) - [Наши друзья](https://anotherreflections.ru/nashi-druzya/) - [Контакты](https://anotherreflections.ru/kontakty/) +- [Политика конфиденциальности](https://anotherreflections.ru/privacy/) ## Игровые форумы diff --git a/public/wp-content/uploads/2014/12/NY-baner-150x60.png b/public/wp-content/uploads/2014/12/NY-baner-150x60.png new file mode 100644 index 0000000..017a7e9 Binary files /dev/null and b/public/wp-content/uploads/2014/12/NY-baner-150x60.png differ diff --git a/public/wp-content/uploads/2014/12/NY-baner-300x51.png b/public/wp-content/uploads/2014/12/NY-baner-300x51.png new file mode 100644 index 0000000..c5bb75c Binary files /dev/null and b/public/wp-content/uploads/2014/12/NY-baner-300x51.png differ diff --git a/public/wp-content/uploads/2014/12/NY-baner.png b/public/wp-content/uploads/2014/12/NY-baner.png new file mode 100644 index 0000000..53ddc84 Binary files /dev/null and b/public/wp-content/uploads/2014/12/NY-baner.png differ diff --git a/public/wp-content/uploads/header_bg.gif b/public/wp-content/uploads/header_bg.gif new file mode 100644 index 0000000..ccd4e68 Binary files /dev/null and b/public/wp-content/uploads/header_bg.gif differ diff --git a/scripts/migrate-wp.mjs b/scripts/migrate-wp.mjs index 4e9c13e..8376dc8 100644 --- a/scripts/migrate-wp.mjs +++ b/scripts/migrate-wp.mjs @@ -28,15 +28,22 @@ const decodeEntities = (s) => s .replace(/>/g, '>') .replace(/&/g, '&'); +/** Заменить старые WP-uploads URL на относительные (мы скачали в public/wp-content/uploads/). */ +const rewriteWpUploads = (url) => + url.replace(/^https?:\/\/anotherreflections\.ru(\/wp-content\/uploads\/.+)$/i, '$1'); + const htmlToMd = (html) => { if (!html) return ''; let s = html; - // images + // Сначала "вложенные" — превращаем в один markdown-image со ссылкой + s = s.replace(/]*?href=["']([^"']+)["'][^>]*>\s*]*?src=["']([^"']+)["'][^>]*?(?:alt=["']([^"']*)["'])?[^>]*?\/?>\s*<\/a>/gi, + (_, href, src, alt) => `[![${alt || ''}](${rewriteWpUploads(src)})](${rewriteWpUploads(href)})`); + // одиночные images s = s.replace(/]*?src=["']([^"']+)["'][^>]*?(?:alt=["']([^"']*)["'])?[^>]*?\/?>/gi, - (_, src, alt) => `![${alt || ''}](${src})`); + (_, src, alt) => `![${alt || ''}](${rewriteWpUploads(src)})`); // links s = s.replace(/]*?href=["']([^"']+)["'][^>]*>([\s\S]*?)<\/a>/gi, - (_, href, text) => `[${text.replace(/<[^>]+>/g,'').trim()}](${href})`); + (_, href, text) => `[${text.replace(/<[^>]+>/g,'').trim()}](${rewriteWpUploads(href)})`); // bold s = s.replace(/<(strong|b)\b[^>]*>([\s\S]*?)<\/\1>/gi, '**$2**'); // italic @@ -106,7 +113,12 @@ for (const p of data.posts) { categories: p.categories.map(c => c.name), categorySlugs: p.categories.map(c => c.slug), tags: p.tags.map(t => t.name), - description: (htmlToMd(p.excerpt) || htmlToMd(p.content_html)).slice(0, 200).replace(/\s+/g,' ').trim(), + description: (htmlToMd(p.excerpt) || htmlToMd(p.content_html)) + .replace(/!?\[([^\]]*)\]\([^)]+\)/g, '$1') // strip markdown images/links для excerpt + .replace(/[*_>#]+/g, '') // strip md formatting символов + .replace(/\s+/g, ' ') + .trim() + .slice(0, 200), }; const body = htmlToMd(p.content_html); const out = `${fmtFrontmatter(fm)}\n\n${body}\n`; diff --git a/src/components/Analytics.astro b/src/components/Analytics.astro new file mode 100644 index 0000000..9413071 --- /dev/null +++ b/src/components/Analytics.astro @@ -0,0 +1,37 @@ +--- +import { ANALYTICS } from '../consts'; +const { yandexMetrika, googleGtag } = ANALYTICS; +--- + + + diff --git a/src/consts.ts b/src/consts.ts index a51a7c5..000a56d 100644 --- a/src/consts.ts +++ b/src/consts.ts @@ -5,6 +5,11 @@ export const SOCIAL = { export const CONTACT_EMAIL = 'info@anotherreflections.ru'; +export const ANALYTICS = { + yandexMetrika: '13938862', // counter ID, webvisor включён + googleGtag: 'GT-PH39R3X', // GTM/GA4 measurement +}; + /** Русская плюрализация: [1, 2-4, 5+]. Пример: plural(8, ['мир', 'мира', 'миров']) → 'миров' */ export function plural(n: number, forms: [string, string, string]): string { const a = Math.abs(n) % 100; diff --git a/src/content/posts/c-nastupayushhim-novym-2015-godom-i-rozhdestvom.md b/src/content/posts/c-nastupayushhim-novym-2015-godom-i-rozhdestvom.md index 04fdd26..acf79da 100644 --- a/src/content/posts/c-nastupayushhim-novym-2015-godom-i-rozhdestvom.md +++ b/src/content/posts/c-nastupayushhim-novym-2015-godom-i-rozhdestvom.md @@ -8,10 +8,10 @@ categories: - "Общие новости" categorySlugs: - "obshhie-novosti" -description: "[![](http://anotherreflections.ru/wp-content/uploads/2014/12/NY-baner-300x51.png)](http://anotherreflections.ru/wp-content/uploads/2014/12/NY-baner.png) Подходит к своему завершению 2014 год." +description: "[![](/wp-content/uploads/2014/12/NY-baner-300x51.png)](/wp-content/uploads/2014/12/NY-baner.png) Подходит к своему завершению 2014 год. В жизни каждого из нас произошло много событий - ярких," --- -[![](http://anotherreflections.ru/wp-content/uploads/2014/12/NY-baner-300x51.png)](http://anotherreflections.ru/wp-content/uploads/2014/12/NY-baner.png) +[![](/wp-content/uploads/2014/12/NY-baner-300x51.png)](/wp-content/uploads/2014/12/NY-baner.png) diff --git a/src/content/posts/11.md b/src/content/posts/kto-my.md similarity index 99% rename from src/content/posts/11.md rename to src/content/posts/kto-my.md index 193c3dd..68232f0 100644 --- a/src/content/posts/11.md +++ b/src/content/posts/kto-my.md @@ -2,7 +2,7 @@ title: "Кто мы?" pubDate: "2013-08-25T03:15:01+03:00" updatedDate: "2013-10-06T02:05:52+03:00" -slug: "11" +slug: "kto-my" legacyId: "365" author: "admin" categories: diff --git a/src/content/posts/95.md b/src/content/posts/s-23-fevralya-2011.md similarity index 97% rename from src/content/posts/95.md rename to src/content/posts/s-23-fevralya-2011.md index 20e5d8f..7367b30 100644 --- a/src/content/posts/95.md +++ b/src/content/posts/s-23-fevralya-2011.md @@ -2,7 +2,7 @@ title: "С 23 февраля!" pubDate: "2011-02-23T00:48:39+03:00" updatedDate: "2013-03-29T14:34:37+03:00" -slug: "95" +slug: "s-23-fevralya-2011" legacyId: "95" author: "admin" categories: diff --git a/src/content/posts/152.md b/src/content/posts/s-nastupayushhim-novym-2012-godom.md similarity index 96% rename from src/content/posts/152.md rename to src/content/posts/s-nastupayushhim-novym-2012-godom.md index a579a08..04a6f68 100644 --- a/src/content/posts/152.md +++ b/src/content/posts/s-nastupayushhim-novym-2012-godom.md @@ -2,7 +2,7 @@ title: "С наступающим Новым 2012 годом!" pubDate: "2012-01-01T00:27:00+03:00" updatedDate: "2013-03-29T14:35:17+03:00" -slug: "152" +slug: "s-nastupayushhim-novym-2012-godom" legacyId: "152" author: "admin" categories: diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index 41cfcc3..a768c73 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -3,6 +3,8 @@ import '../styles/global.css'; import { SITE_TITLE, SITE_DESCRIPTION, SITE_URL, SITE_LANG, MAIN_NAV } from '../consts'; import BrandMark from '../components/BrandMark.astro'; import SocialLinks from '../components/SocialLinks.astro'; +import Analytics from '../components/Analytics.astro'; +import CookieConsent from '../components/CookieConsent.astro'; interface Props { title?: string; @@ -39,6 +41,8 @@ const year = new Date().getFullYear(); + + -
+
diff --git a/src/pages/index.astro b/src/pages/index.astro index fb9fb99..16eb7c8 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -21,7 +21,7 @@ const fmtDate = (d: Date) =>
{totalWorlds} {plural(totalWorlds, ['мир', 'мира', 'миров'])} {totalPosts} {plural(totalPosts, ['публикация', 'публикации', 'публикаций'])} - {new Date().getFullYear() - SITE_FOUNDED} {plural(new Date().getFullYear() - SITE_FOUNDED, ['год', 'года', 'лет'])} онлайн + с {SITE_FOUNDED} года
diff --git a/src/pages/privacy.astro b/src/pages/privacy.astro new file mode 100644 index 0000000..116d288 --- /dev/null +++ b/src/pages/privacy.astro @@ -0,0 +1,96 @@ +--- +import BaseLayout from '../layouts/BaseLayout.astro'; +import { CONTACT_EMAIL, SITE_URL } from '../consts'; +const lastUpdated = '20 мая 2026 г.'; +--- + +
+
+

Политика конфиденциальности

+ +
+ +
+

+ Настоящая Политика описывает, какие данные собирает сайт anotherreflections.ru + (далее — «Сайт»), для чего они используются и как с ними обращаются. Политика составлена + в соответствии с Федеральным законом от 27.07.2006 № 152-ФЗ «О персональных данных». +

+ +

1. Какие данные мы собираем

+

Сайт не требует регистрации и не запрашивает у посетителя ни имени, ни телефона, ни почты.

+

+ При посещении сайта автоматически собираются обезличенные технические данные через + сервисы статистики: +

+
    +
  • Яндекс.Метрика — IP-адрес (обезличенный), тип устройства и браузера, + источник перехода, поведение на страницах, в том числе запись действий (Вебвизор).
  • +
  • Google Analytics — аналогичный обезличенный набор технических данных + о посещении.
  • +
+

+ Эти данные собираются только после того, как вы согласились на использование + статистики через всплывающее уведомление о cookies. Если вы выбрали «Только необходимые», + статистика не загружается. +

+ +

2. Cookies

+

Сайт использует следующие cookies:

+
    +
  • Технические — для запоминания вашего выбора по уведомлению о cookies + (cookie ar-consent, срок 12 месяцев). Без согласия не устанавливаются никакие + другие.
  • +
  • Аналитические (после согласия) — устанавливаются скриптами + Яндекс.Метрики и Google Analytics для подсчёта уникальных посетителей и анализа + поведения. Подробнее — на страницах + Условия использования Яндекс.Метрики + и Политика конфиденциальности Google.
  • +
+ +

3. Зачем мы это делаем

+

Статистика помогает нам понимать:

+
    +
  • сколько людей и откуда приходит на сайт;
  • +
  • какие разделы и публикации читают чаще;
  • +
  • какие страницы работают плохо (медленно загружаются, неудобны на мобильном).
  • +
+

На основе этого мы улучшаем сайт. Данные используются в обезличенном виде и не позволяют идентифицировать конкретного человека.

+ +

4. Передача данных третьим лицам

+

+ Обезличенные технические данные передаются операторам сервисов статистики + (Яндекс, Google) — это необходимо для работы самих сервисов. Иным третьим лицам ничего не передаётся. +

+ +

5. Ваши права

+

Вы можете в любой момент:

+
    +
  • Отозвать согласие на статистику — очистите cookies сайта или удалите ar-consent + в DevTools. При следующем визите снова появится уведомление, выберите «Только необходимые».
  • +
  • Запросить удаление ваших данных из систем статистики — обратитесь напрямую к операторам + (Яндекс, + Google).
  • +
  • Связаться с администрацией сайта по адресу {CONTACT_EMAIL} + по любым вопросам, связанным с данными.
  • +
+ +

6. Изменения

+

+ Мы можем обновлять эту Политику. Актуальная версия всегда доступна по адресу + /privacy/. Дата последнего обновления указана в начале документа. +

+ +

7. Контакты

+

+ По вопросам обработки персональных данных: + {CONTACT_EMAIL}. +

+
+
+
diff --git a/src/pages/sitemap.txt.ts b/src/pages/sitemap.txt.ts index 11a3103..509da48 100644 --- a/src/pages/sitemap.txt.ts +++ b/src/pages/sitemap.txt.ts @@ -10,6 +10,7 @@ export async function GET(_context: APIContext) { '/', '/miry/', '/kontakty/', + '/privacy/', ]; // Категории — собираем из постов diff --git a/src/styles/global.css b/src/styles/global.css index f086fd0..5d68465 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -412,7 +412,7 @@ pre { .post-body { font-size: 1.05rem; } -.post-body p:first-of-type::first-letter { +.post-body.has-dropcap p:first-of-type::first-letter { font-family: var(--font-serif); font-size: 3.2em; float: left; @@ -563,6 +563,64 @@ pre { line-height: 1.5; } +/* ============================== COOKIE CONSENT ============================== */ +.cookie-consent { + position: fixed; + left: 1rem; + right: 1rem; + bottom: 1rem; + max-width: 720px; + margin: 0 auto; + padding: 1.1rem 1.4rem; + background: rgba(17, 20, 29, 0.96); + backdrop-filter: blur(10px); + border: 1px solid var(--border-strong); + border-radius: 10px; + box-shadow: 0 12px 40px rgba(0, 0, 0, 0.6); + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 1rem; + z-index: 60; + font-size: .9rem; +} +.cookie-text { flex: 1 1 320px; } +.cookie-text p { margin: 0; color: var(--fg-muted); line-height: 1.5; } +.cookie-text a { color: var(--link); } +.cookie-actions { + display: flex; + gap: .55rem; + flex-wrap: wrap; +} +.cookie-btn { + padding: .55em 1.1em; + border-radius: 6px; + border: 1px solid var(--border); + background: var(--bg-elev); + color: var(--fg); + font-family: inherit; + font-size: .88rem; + font-weight: 500; + cursor: pointer; + transition: border-color .2s, background .2s, color .2s; +} +.cookie-btn:hover { border-color: var(--accent); color: var(--accent); } +.cookie-btn-primary { + background: var(--accent-deep); + border-color: var(--accent-deep); + color: #fff; +} +.cookie-btn-primary:hover { + background: var(--accent); + border-color: var(--accent); + color: #fff; +} + +@media (max-width: 640px) { + .cookie-consent { padding: .9rem 1rem; left: .5rem; right: .5rem; bottom: .5rem; } + .cookie-actions { width: 100%; justify-content: flex-end; } +} + /* ============================== SOCIAL RAIL ============================== */ .social-rail { position: fixed;