323 lines
16 KiB
Markdown
323 lines
16 KiB
Markdown
# План миграции сайта 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-подобная методология
|
||
|
||
---
|
||
|
||
## Принятые решения
|
||
|
||
- **URL статей**: формат `/articles/2019-05-01-predictor/`, slug = имя файла без расширения
|
||
- **Пакет `@anwinged/predictor`**: чистый TypeScript, без привязки к фреймворку — миграция на Vue 3 затрагивает только обёртку
|
||
- **Tailwind CSS**: используем; Tailwind 4 делает tree-shaking из коробки — в сборку попадают только используемые утилиты
|
||
- **Текст на главной**: переносим как есть, автор обновит сам
|
||
- **Галерея**: один альбом с фотографиями, хранение в `src/content/gallery/` (оптимизация через Astro Image)
|
||
- **Локальная разработка**: всё через Docker (и dev, и build), npm на хосте не используем
|
||
|
||
## Целевое состояние
|
||
|
||
### Стек
|
||
- **Генератор**: Astro
|
||
- **Стилизация**: Tailwind CSS 4
|
||
- **Интерактивность**: 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 4
|
||
- Установить и настроить Tailwind 4 (tree-shaking из коробки — в сборку попадает только используемый CSS)
|
||
- Перенести дизайн-токены из SCSS-переменных в Tailwind-конфигурацию (через `@theme` в CSS):
|
||
- Шрифты: 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-prod`: `astro build`
|
||
- Добавить задачу `dev`: запуск `astro dev` для локальной разработки (через Docker с проброшенными портами)
|
||
- Обновить задачу `deploy`: путь к собранным файлам `dist/`
|
||
- Сохранить структуру Docker-команд через Taskfile
|
||
- Все npm/astro-команды выполняются внутри Docker-контейнера
|
||
|
||
---
|
||
|
||
### Этап 2. Лейауты и компоненты
|
||
|
||
#### 2.1. Создать базовый лейаут `BaseLayout.astro`
|
||
Аналог текущего `base.html.twig`. Должен включать:
|
||
- `<html lang="ru">`, мета-теги (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`
|
||
- Заголовок статьи `<h1>`
|
||
- Дата публикации и 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(),
|
||
keywords: z.array(z.string()).optional(),
|
||
tags: z.array(z.string()).optional(),
|
||
draft: z.boolean().default(false),
|
||
// date — парсится из имени файла (YYYY-MM-DD-slug.md), не хранится в frontmatter
|
||
}
|
||
```
|
||
|
||
#### 3.2. Перенести статьи
|
||
Перенести 8 markdown-файлов из `source/_articles/` в `src/content/articles/`.
|
||
|
||
Имена файлов сохраняют даты (формат `YYYY-MM-DD-slug.md`) для удобной навигации в файловой системе. Дата парсится из имени файла при сборке (в `content.config.ts` или в `getStaticPaths`), дублировать в frontmatter не нужно.
|
||
|
||
Необходимые изменения в каждом файле:
|
||
- Удалить Sculpin-специфичные поля frontmatter (`layout`, `styles`, `scripts`, `use`)
|
||
- Оставить `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-компонент `<div id="app"></div>`.
|
||
|
||
Шаги:
|
||
- Переименовать файл в `.mdx`
|
||
- Обновить Vue-компонент `PredictorDemo.vue` до Vue 3 (Composition API или Options API)
|
||
- `@anwinged/predictor` — чистый TS, совместимость с Vue 3 не затронута
|
||
- Перенести scoped-стили компонента на Tailwind или оставить scoped CSS
|
||
- В MDX-файле: импортировать компонент и использовать `<PredictorDemo client:visible />`
|
||
- Удалить ручное подключение 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`
|
||
- Content Collection `gallery` — фотографии с метаданными (title, описание, дата, порядок)
|
||
- Структура хранения:
|
||
```
|
||
src/content/gallery/
|
||
├── photo-001.md # frontmatter: title, image, order
|
||
├── photo-002.md
|
||
└── ...
|
||
public/photos/
|
||
├── photo-001.jpg # оригиналы фотографий
|
||
├── photo-002.jpg
|
||
└── ...
|
||
```
|
||
- Сетка превью с оптимизацией через Astro Image (WebP/AVIF, responsive sizes)
|
||
- Просмотр полноразмерных фотографий: lightbox-библиотека (PhotoSwipe или GLightbox)
|
||
- Один альбом, без категорий
|
||
|
||
---
|
||
|
||
### Этап 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/2019-05-01-predictor/`.
|
||
Slug = имя файла без расширения, Astro использует его как есть.
|
||
|
||
Редиректы в nginx для старых URL (301):
|
||
```
|
||
/articles/2019/05/01/predictor/ → /articles/2019-05-01-predictor/
|
||
/articles/2019/06/01/php-serialization/ → /articles/2019-06-01-php-serialization/
|
||
/articles/2019/06/28/storytelling/ → /articles/2019-06-28-storytelling/
|
||
/articles/2019/08/08/yandex-disk-image-hosting/ → /articles/2019-08-08-yandex-disk-image-hosting/
|
||
/articles/2019/09/26/highload-videos/ → /articles/2019-09-26-highload-videos/
|
||
/articles/2020/06/27/interesting-programming-blogs/ → /articles/2020-06-27-interesting-programming-blogs/
|
||
/articles/2020/06/27/type-discriminant/ → /articles/2020-06-27-type-discriminant/
|
||
/articles/2020/11/08/nullable-fields/ → /articles/2020-11-08-nullable-fields/
|
||
```
|
||
|
||
Редирект `/atom.xml` → `/rss.xml` для совместимости RSS-подписок.
|
||
|
||
---
|
||
|
||
## Порядок выполнения
|
||
|
||
```
|
||
Этап 1 (инициализация) → Этап 2 (лейауты) → Этап 3 (контент)
|
||
→ Этап 4 (страницы) → Этап 5 (RSS/sitemap) → Этап 6 (деплой) → Этап 7 (очистка)
|
||
```
|
||
|
||
Этапы 2 и 3 можно частично выполнять параллельно.
|
||
Этап 7 выполняется только после полной проверки нового сайта.
|