# Деплой на shared-хостинг (чек-лист, V.2) Дата: 2026-08-06. Выполняет ВЛАДЕЛЕЦ руками (V.1: агент прод не трогает). Проходить целиком, частичные варианты запрещены (gotcha-partial-build). ## Артефакты деплоя (ВСЕ, их ТРИ) 1. **Статика сайта**: `apps/web/dist/` → web root хостинга (например, `public_html/`). Сборка: `npm ci && npm run build` в `apps/web` (Node 20+, локально) либо взять готовый dist из превью-версии. 2. **API**: содержимое `server/` раскладывается по web root ЧАСТЯМИ (не целиком!): `server/api/` → `public_html/api/`, `server/lib/` → `public_html/lib/`. Эндпоинты оказываются доступны как `/api/*.php`. Пути жёсткие: PHP-файлы ищут lib как `../lib/` (smoke-тест локально поднимает docroot = server/, поэтому на проде web root = dist + содержимое server/). 3. **Конфиг + БД**: скопировать `server/config.example.php` → `public_html/config.php` (именно в КОРЕНЬ web root — db.php ищет его как `__DIR__.'/../config.php'` от lib/), выставить `db_path` — путь ВНЕ web root (например, `/home//go-learn.sqlite`). Сам файл БД создастся сам при первом обращении (права на каталог — записываемые для PHP). Также выставить `base_url` (полный URL сайта — идёт в письма восстановления пароля) и `mail_from` (отправитель писем). ## Итоговая структура на хостинге ```text public_html/ ├── index.html, learn.html, play.html, … ← из dist (артефакт 1) ├── _astro/, assets/ ← из dist ├── .htaccess ← из dist + блок запрета ниже ├── api/ ← server/api/ (артефакт 2) │ ├── health.php, register.php, login.php, logout.php, me.php, │ ├── progress.php, captcha.php │ └── password-reset/ (request.php, confirm.php) ├── lib/ ← server/lib/ │ └── auth.php, db.php, http.php, ratelimit.php └── config.php ← созданный вручную (артефакт 3) ``` В корневой `.htaccess` (из dist) дописать блок из server/.htaccess: ```apache Require all denied ``` ## Порядок (каждый шаг — отдельное действие) 0. Проверки хостинга: PHP ≥ 8.0, модули pdo_sqlite, sqlite3 (phpinfo в панели или `php -v`/`php -m` по SSH, если есть). Нет pdo_sqlite — стоп, вопрос хостеру. 1. Залить dist (артефакт 1). 2. Залить server/ (артефакт 2) и создать config.php (артефакт 3). 3. Smoke: открыть `/api/health.php` → ожидается `{"status":"ok"}`. Не 200/не JSON — смотреть логи PHP в панели, НЕ править код на глазах. 4. Smoke вручную: регистрация на странице /register.html (с капчей) → вход → «Синхронизировать прогресс» → статус «Прогресс отправлен». 4.1. Проверка почты (этап 8.1): «Забыли пароль?» на /account.html → письмо со ссылкой приходит и ссылка ведёт на /password-reset.html?token=…. Не приходит — смотреть настройки SPF/DKIM в панели хостинга; до решения восстановление считать нерабочим (риск в PROJECT_STATE). 5. Проверить, что БД-файл появился по пути из config.php и НЕ доступен по HTTP (попытка открыть его URL → 403/404). 6. HTTPS — включать ТОЛЬКО панелью хостинга (в .htaccess редиректа нет намеренно, gotcha-https-redirect-proxy). После включения — повторить шаги 3–4 по HTTPS. ## Канонические пути (V.5) — заполнить при первом деплое - Web root: `____________` - URL сайта: `____________` - Путь БД (вне web root): `____________` - Дата первого деплоя: `____________` ## Откат Откат = перезаливка предыдущего dist и (при изменении server/) предыдущего server/. Схема БД этапа 8 аддитивна (init-only), обратной миграции нет; перед любым изменением схемы в будущем — дамп БД ДО деплоя (V.3). ## Бэкап и restore (V.7, минимум) - Бэкап = копия файла SQLite (из панели файлового менеджера/SSH) + config.php. Периодичность — после заметного роста числа пользователей (ручная, SoT расписания — этот файл). - Restore: остановить запись (выключить сайт в панели) → вернуть файл БД на путь из config.php → smoke шаг 3–4. Проверка restore выполняется на первом же бэкапе: распаковать копию под другим именем и открыть `sqlite3 <файл> "SELECT count(*) FROM users;"` (или через панель). ## Чего НЕ делать - Не класть config.php и БД в web root. - Не включать HTTPS-редирект в .htaccess (только панель). - Не править PHP/БД на проде напрямую: правка → локальный smoke.sh → перезаливка.