diff --git a/spec/migration.md b/spec/migration.md new file mode 100644 index 0000000..d03bd32 --- /dev/null +++ b/spec/migration.md @@ -0,0 +1,291 @@ +# План миграции сайта vakhrushev.me с Sculpin на Astro + +## Обзор текущего состояния + +### Стек +- **Генератор**: Sculpin 3 (PHP 8) +- **Шаблоны**: Twig (base → internal → article, три уровня наследования) +- **Фронтенд**: Webpack 4, SCSS, Vue 2 (интерактивная демка гадалки) +- **Сборка**: Docker (PHP + Node контейнеры), Taskfile +- **Деплой**: Docker-образ nginx, Ansible на личный сервер +- **Аналитика**: Яндекс.Метрика +- **Шрифты**: Google Fonts (PT Serif, Source Code Pro), Font Awesome 5 +- **Кастомные бандлы Sculpin**: HtmlPrettier, SiteMap, TwigExtension (hashed_asset) + +### Контент +- 8 статей в markdown (2019-2020), русский язык +- 1 статья с интерактивным Vue-компонентом (гадалка Шеннона) +- Страницы: главная, список статей, 404 +- Atom-лента (ручной шаблон) +- Sitemap (кастомный бандл + шаблон) +- robots.txt + +### Структура стилей +- Переменные SCSS: шрифт PT Serif 20px, ширина 740px, цвета GitHub +- Базовые стили, стили главной, стили внутренних страниц, навигация +- BEM-подобная методология + +--- + +## Целевое состояние + +### Стек +- **Генератор**: Astro +- **Стилизация**: Tailwind CSS +- **Интерактивность**: Vue 3 (islands, для гадалки и будущих компонентов) +- **Сборка**: Docker (один Node-контейнер), Taskfile +- **Деплой**: Docker-образ nginx, Ansible (без изменений) + +### Разделы сайта +1. **Главная** — краткое описание, ссылки +2. **Блог** — статьи и эссе по программированию +3. **Галерея** — фотографии (новый раздел, реализуется позже) + +--- + +## Задачи + +### Этап 1. Инициализация проекта Astro + +#### 1.1. Создать проект Astro +- Инициализировать Astro в корне проекта (или в отдельной ветке) +- Настроить `astro.config.mjs`: site URL `https://vakhrushev.me`, язык `ru` +- Установить интеграции: `@astrojs/sitemap`, `@astrojs/rss`, `@astrojs/vue`, `@astrojs/tailwind` +- Настроить TypeScript (strict или relaxed — на усмотрение) + +#### 1.2. Настроить Tailwind CSS +- Установить и настроить Tailwind +- Перенести дизайн-токены из SCSS-переменных в Tailwind-конфигурацию: + - Шрифты: PT Serif (serif), Source Code Pro (mono) + - Цвета: `#24292e` (текст), `#0366d6` (ссылки), `#e6e6e6` (линии) + - Ширина контента: 740px + - Размер шрифта: 20px базовый + +#### 1.3. Настроить Docker для сборки +- Заменить два Docker-образа (PHP + Node) одним Node-образом + - Использовать актуальную LTS-версию Node (22) +- Удалить PHP Dockerfile — он больше не нужен +- Сохранить `Dockerfile.nginx.prod`, обновить путь: `dist/` вместо `output_prod/` + +#### 1.4. Обновить Taskfile +- Убрать задачи: `composer`, `sculpin`, `shell-node`, `format-php` +- Обновить задачи сборки: + - `build-dev`: `astro build` (dev-режим — через `astro dev`) + - `build-prod`: `astro build` +- Обновить задачу `deploy`: путь к собранным файлам `dist/` +- Добавить задачу `dev`: запуск `astro dev` для локальной разработки +- Сохранить структуру Docker-команд через Taskfile + +--- + +### Этап 2. Лейауты и компоненты + +#### 2.1. Создать базовый лейаут `BaseLayout.astro` +Аналог текущего `base.html.twig`. Должен включать: +- ``, мета-теги (charset, viewport, description, keywords) +- Open Graph мета-теги (og:site_name, og:title, og:description, og:url, og:locale) +- Подключение шрифтов Google Fonts (PT Serif, Source Code Pro) +- Яндекс.Метрика (вынести в отдельный компонент `YandexMetrika.astro`) +- Yandex verification мета-тег +- Слот для контента + +Параметры (props): `title`, `description`, `keywords` + +#### 2.2. Создать лейаут для внутренних страниц `InternalLayout.astro` +Аналог `internal.html.twig`: +- Использует `BaseLayout` +- Навигация (компонент `Navigation.astro`) +- Контейнер `.page` с ограничением ширины + +#### 2.3. Создать лейаут для статей `ArticleLayout.astro` +Аналог `article.html.twig`: +- Использует `InternalLayout` +- Заголовок статьи `

` +- Дата публикации и email внизу +- Слот для содержимого статьи + +#### 2.4. Создать общие компоненты +- `Navigation.astro` — навигация (Главная, Блог, Галерея) +- `YandexMetrika.astro` — код счетчика аналитики +- `ArticleList.astro` — список статей, сгруппированных по годам + +Особенность: Font Awesome больше не нужен (социальные ссылки удалены в коммите `63a324c`). +Если понадобятся иконки — использовать `astro-icon` или inline SVG. + +--- + +### Этап 3. Content Collections и перенос статей + +#### 3.1. Настроить Content Collection для статей +- Создать `src/content/articles/` для markdown-файлов +- Определить схему в `src/content.config.ts`: + ```ts + { + title: z.string(), + description: z.string().optional(), + date: z.date(), // извлечь из имени файла или добавить в frontmatter + keywords: z.array(z.string()).optional(), + tags: z.array(z.string()).optional(), + draft: z.boolean().default(false), + } + ``` + +#### 3.2. Перенести статьи +Перенести 8 markdown-файлов из `source/_articles/` в `src/content/articles/`. + +Необходимые изменения в каждом файле: +- Удалить Sculpin-специфичные поля frontmatter (`layout`, `styles`, `scripts`, `use`) +- Добавить поле `date` (сейчас дата в имени файла, нужно продублировать в frontmatter) +- Оставить `title`, `description`, `keywords` +- Опционально: добавить `tags` для будущей фильтрации + +Список статей: +1. `2019-05-01-predictor.md` — **ОСОБЫЙ СЛУЧАЙ**, см. задачу 3.3 +2. `2019-06-01-php-serialization.md` +3. `2019-06-28-storytelling.md` +4. `2019-08-08-yandex-disk-image-hosting.md` +5. `2019-09-26-highload-videos.md` +6. `2020-06-27-interesting-programming-blogs.md` +7. `2020-06-27-type-discriminant.md` +8. `2020-11-08-nullable-fields.md` + +#### 3.3. Перенести статью с интерактивным компонентом (гадалка) +Статья `predictor.md` содержит встроенный Vue-компонент `
`. + +Шаги: +- Переименовать файл в `.mdx` +- Обновить Vue-компонент `PredictorDemo.vue` до Vue 3 (Composition API или Options API) + - `@anwinged/predictor` — проверить совместимость с Vue 3, при необходимости обновить + - Перенести scoped-стили компонента на Tailwind или оставить scoped CSS +- В MDX-файле: импортировать компонент и использовать `` +- Удалить ручное подключение JS/CSS через frontmatter (`styles`, `scripts`) + +--- + +### Этап 4. Страницы + +#### 4.1. Главная страница `src/pages/index.astro` +- Использовать `BaseLayout` +- Имя, краткое описание (обновить текст — актуализировать информацию о себе) +- Ссылки: email, git, другие +- Список последних статей (через `getCollection('articles')`) + +#### 4.2. Страница списка статей `src/pages/articles/index.astro` +- Использовать `InternalLayout` +- Список всех статей, сгруппированных по годам (как сейчас в `article_list.twig`) +- Сортировка по дате, от новых к старым + +#### 4.3. Динамические страницы статей `src/pages/articles/[...slug].astro` +- Использовать `ArticleLayout` +- Рендеринг markdown/MDX содержимого через `render()` +- SEO мета-теги из frontmatter + +#### 4.4. Страница 404 `src/pages/404.astro` +- Использовать `InternalLayout` +- Текст "Страница не найдена" и ссылка на главную + +#### 4.5. Страница галереи `src/pages/gallery/index.astro` (заглушка) +- Использовать `InternalLayout` +- Placeholder-страница "Скоро здесь будут фотографии" +- Полноценная реализация — отдельная задача в будущем + +--- + +### Этап 5. RSS, Sitemap, SEO + +#### 5.1. Настроить RSS-ленту +- Создать `src/pages/rss.xml.ts` с использованием `@astrojs/rss` +- Включить все статьи (title, description, date, link) +- URL: `/rss.xml` +- Для обратной совместимости: добавить редирект `/atom.xml` → `/rss.xml` (в nginx или через Astro) + +#### 5.2. Настроить Sitemap +- Интеграция `@astrojs/sitemap` уже генерирует sitemap автоматически +- Убедиться, что 404 и служебные страницы исключены + +#### 5.3. Настроить robots.txt +- Создать `public/robots.txt` со ссылкой на sitemap + +#### 5.4. Open Graph и мета-теги +- Уже реализовано в BaseLayout (задача 2.1) +- Проверить корректность на каждом типе страниц + +--- + +### Этап 6. Сборка и деплой + +#### 6.1. Обновить Docker-сборку +- Новый `docker/node/Dockerfile` на Node 22 LTS +- PHP Dockerfile удалить +- Обновить `Dockerfile.nginx.prod`: + ```dockerfile + FROM nginx:stable + COPY dist /usr/share/nginx/html + ``` +- Добавить конфиг nginx для SPA fallback (404.html) и кэширования статики + +#### 6.2. Обновить Taskfile +Финальная версия задач: +```yaml +tasks: + dev: # astro dev (локальная разработка) + build-prod: # astro build + deploy: # build-prod → docker build → ansible +``` + +#### 6.3. Проверить деплой +- Собрать Docker-образ +- Проверить, что все страницы отдаются корректно +- Проверить 404 +- Проверить RSS и sitemap + +--- + +### Этап 7. Очистка + +#### 7.1. Удалить файлы Sculpin +- `app/` — SculpinKernel, конфиги +- `bundle/` — кастомные бандлы (HtmlPrettier, SiteMap, TwigExtension) +- `source/` — старые шаблоны, ассеты, контент (после проверки, что всё перенесено) +- `composer.json`, `composer.lock` +- `docker/php/Dockerfile` +- `webpack.config.js` +- `.php-cs-fixer.php` + +#### 7.2. Обновить package.json +- Удалить старые зависимости (webpack, babel, vue 2, node-sass, и т.д.) +- Удалить старые npm-скрипты +- Оставить только Astro и его зависимости + +#### 7.3. Обновить .gitignore +- Убрать `output_dev/`, `output_prod/`, `vendor/` +- Добавить `dist/`, `.astro/`, `node_modules/` + +#### 7.4. Обновить README.md +- Описание нового стека +- Инструкции по локальной разработке и деплою + +--- + +## Сохранение URL-совместимости + +Текущие URL статей имеют формат: `/articles/YYYY/MM/DD/slug/` +(например, `/articles/2019/05/01/predictor/`). + +Нужно сохранить эти URL, чтобы не сломать внешние ссылки. Варианты: +1. **Настроить slug в Astro** — использовать `getStaticPaths()` с кастомными slug, повторяющими текущую структуру +2. **Редиректы в nginx** — если хочется упростить URL-структуру (например, `/articles/predictor/`), настроить 301-редиректы со старых URL + +Рекомендация: упростить URL до `/articles/slug/` и настроить редиректы в nginx для старых URL. + +--- + +## Порядок выполнения + +``` +Этап 1 (инициализация) → Этап 2 (лейауты) → Этап 3 (контент) + → Этап 4 (страницы) → Этап 5 (RSS/sitemap) → Этап 6 (деплой) → Этап 7 (очистка) +``` + +Этапы 2 и 3 можно частично выполнять параллельно. +Этап 7 выполняется только после полной проверки нового сайта.