# Админка и форма ENTHELOS

## Запуск

Нужен Node.js 22.18 или новее (с модулем node:sqlite). Сервер обслуживает существующий `public`, блог, `/admin/` и API. Вёрстка остальных страниц сохранена.

```sh
npm ci
npm run admin:password
npm start
```

Открыть `http://127.0.0.1:8843/admin/`. Команда `admin:password` создаёт случайный пароль, показывает его один раз и записывает только scrypt-хеш в `.env`. Сохраните пароль в менеджере паролей. При повторном запуске команды пароль меняется; затем перезапустите сервер. Для текущего локального запуска пароль сохранён в `ADMIN-ACCESS.local.txt` (не включать в архив или репозиторий).

## Редактирование

- Создайте статью, укажите заголовок, язык и адрес латиницей через дефисы.
- Добавьте краткое описание и обложку. В текстовом редакторе есть заголовки, списки, цитаты, ссылки, жирный, курсив, подчёркивание и очистка форматирования.
- Чередуйте текстовые блоки и медиаблоки; перемещайте их стрелками. В галерее выберите плитки или карусель, загрузите несколько файлов, задайте описания и подписи. Порядок файлов меняется стрелками.
- Поддерживаются JPG, PNG, WebP, GIF, MP4, WebM, до 50 МБ на файл, до 30 файлов на медиаблок. Видео воспроизводится встроенным плеером; автоматического перекодирования нет. Для совместимости используйте MP4 H.264/AAC или WebM.
- «Сохранить» сохраняет черновик; у уже опубликованной статьи обновляет опубликованную версию. «Опубликовать» делает статью доступной на сайте. «Снять с публикации» скрывает статью.
- RU/ET/EN имеют отдельные статьи. Автоматический медицинский перевод не выполняется. Переключатель языков статьи ведёт в блог выбранного языка.
- «Предпросмотр» показывает текущие несохранённые текст и медиа. Несохранённые изменения вызывают предупреждение при уходе со страницы. Одновременное сохранение старой версии из другой вкладки отклоняется.

## SMTP

Отправка — напрямую через Nodemailer и SMTP cPanel; Brevo не используется. Один транспорт создаётся на приложение в `server/mail.js`. Подробности обновления и настройки: [MAIL.md](MAIL.md).

В `.env` заполните (host, port и secure берутся из cPanel → Email Accounts → no-reply@entel.ee → Connect Devices / Configure Mail Client):

```ini
SMTP_HOST=
SMTP_PORT=
SMTP_SECURE=
SMTP_USER=no-reply@entel.ee
SMTP_PASS=
MAIL_FROM=no-reply@entel.ee
MAIL_FROM_NAME=ENTHELOS
MAIL_REPLY_TO=
CONTACT_TO=
MAIL_TEST_TO=
```

Сохраните существующий `CONTACT_TO` — получателя заявок. `MAIL_REPLY_TO` необязателен: для подтверждений используется `CONTACT_TO`, если отдельный адрес не задан. Прежний `SMTP_FROM` поддерживается, но `MAIL_FROM` имеет приоритет. При миграции задайте `MAIL_FROM=no-reply@entel.ee` явно.

465 требует `SMTP_SECURE=true`; 587 требует `SMTP_SECURE=false` и обязательного STARTTLS. Сертификаты проверяются. Пароль хранится только в конфигурации сервера. После изменений нужен Restart Application.

Форма требует телефон, email и описание. Origin, лимит 5 запросов за 15 минут на IP и honeypot сохранены. Email допускает один адрес без заголовков и списка получателей. Заявки не сохраняются в SQLite и не логируются.

Сначала SMTP принимает письмо клинике с Reply-To посетителя. Затем посетителю отправляется подтверждение на языке формы (RU/ET/EN), без описания проблемы и телефона. Письмо подтверждает получение заявки, а не запись на приём. Reply-To подтверждения — контакт клиники.

Ошибка уведомления клиники возвращает 502; подтверждение не отправляется. Если не отправилось только подтверждение, API сохраняет успех заявки и пишет безопасную ошибку в серверный лог. Повторной отправки и очереди нет. При неполной конфигурации сайт продолжает работать, форма возвращает 503. Успешное принятие SMTP не гарантирует доставку во «Входящие».

```sh
npm run mail:test
# Только после явного задания MAIL_TEST_TO:
npm run mail:test -- --send
```

Первая команда проверяет соединение, TLS и авторизацию без письма; вторая отправляет тестовое письмо на `MAIL_TEST_TO`.

## Размещение

Нужен сервер/контейнер с Node.js и постоянным диском. Прежний статический сервер и статический Sites-хостинг сами по себе не выполняют этот backend. Содержимое `public` нельзя просто загрузить на статический хостинг и получить работающие API.

На рабочем сервере задайте `NODE_ENV=production`, `PUBLIC_ORIGIN=https://ваш-домен`, `HOST=127.0.0.1`, запустите Node как службу за HTTPS reverse proxy. Если перед Node ровно один доверенный proxy, задайте `TRUST_PROXY=1`; не открывайте тогда порт Node напрямую наружу. Proxy должен сохранять Host и передавать X-Forwarded-Proto и X-Forwarded-For. Лимит тела запроса proxy — минимум 52 МБ, таймаут отправки — больше 30 секунд. В контейнере задайте `HOST=0.0.0.0` и ограничьте доступ к порту сетью proxy.

`.env` содержит секреты; не публикуйте его, `ADMIN-ACCESS.local.txt`, папку `data` или `node_modules`. Загруженные медиа доступны по своим публичным URL. Используйте только материалы для публикации. Черновики статей доступны только после входа.

`DATA_DIR` задаёт постоянное хранилище (по умолчанию `data`): `site.sqlite` содержит статьи и сессии; `uploads/` — файлы. Делайте резервные копии всей папки при остановленном сервере, включая SQLite WAL/SHM, если они присутствуют. После удаления статьи файлы сохраняются, чтобы не сломать другие публикации. Очистка неиспользуемых файлов выполняется отдельно. Для нескольких экземпляров приложения нужны общие хранилище и лимитер; текущая конфигурация рассчитана на один процесс.

## Проверки

`npm test`: вход/выход, CSRF, публикация и черновики, очистка HTML, конфликт версий, сохранность базы, языковые маршруты, проверка формата загрузок, SMTP-доставка в локальный тестовый сервер, ошибки SMTP и лимит формы. Настоящие письма заказчику тесты не отправляют.

Редактор Quill поставляется локально, без CDN. HTML перед сохранением очищается серверным allowlist; скрипты, iframe, обработчики событий и опасные ссылки удаляются. https://quilljs.com/docs/quickstart/

## Обновление с dist на public в cPanel

В этой версии фронтенд перенесён в `public/`, но URL остаются `/`, `/assets/...`, `/style.css`. `/public/` не является адресом сайта. Админка остаётся в `admin/` и доступна по `/admin/`.

Локально: `npm ci`, `npm run build`, `node --check server/index.js`, `npm test`. Для сборки нужен Python 3; установку `npm ci --omit=dev` используйте только на сервере, куда загружаете уже подготовленный `public/`.

Загрузите `public/`, обновлённый `server/index.js`, `package.json`, `scripts/build.mjs` и `localize.py`. Документацию и `.prettierignore` также обновите. Application root остаётся корнем проекта, Startup file — `server/index.js`, Application URL — корнем домена. После загрузки нажмите Restart Application.

Старый `dist/` больше не используется Node. Сначала сохраните его резервную копию вне публичной области и убедитесь, что серверные правки перенесены в `public/`. Затем старую папку можно удалить. Сохраняйте рабочие `.env`, `data/` и `admin/`.

Не назначайте весь корень проекта публичной директорией Apache. Если на хостинге Document Root отдельно указывает на старый `dist/`, согласуйте его замену на `public/` с конфигурацией Passenger; сам Application root менять на `public/` нельзя. Проверьте существующий `.htaccess` на сервере: локально его нет, поэтому пользовательские RewriteRule на старый путь автоматически не изменялись.
