Облачное подключение: рабочая сборка GitHub Pages создаётся командой
npm run build:pagesи использует общий API/YDB.npm run build:demoсохраняет старый локальный демонстрационный режим и не должен перезаписывать облачную публикацию. Приём реальных заявок пока выключен до замены заглушек политики и согласия. Подробнее:cloud-functions/ydb-stage1/FORM.md.
Первая рабочая серверная версия. Не готовый публичный коммерческий запуск: цены и расход ещё требуют проверки, телефон и фотографии работ — заглушки, юридические документы не оформлены. По умолчанию приём реальных заявок выключен.
/admin: вход по паролю, текущая опубликованная цена рядом с полем новой цены.Нужен Node.js 24 LTS. Сторонних серверных npm-зависимостей нет; SQLite входит в Node.
npm run setup
# Введите новый пароль в скрытом интерактивном запросе.
# Будет создан .env с scrypt-хешем. Сам пароль не записывается.
npm start
Открыть http://localhost:3000/, админка — http://localhost:3000/admin.
HTML нельзя просто открыть двойным щелчком: этому варианту нужен сервер.
Пароль по умолчанию отсутствует. Без настройки хеша сервер откажется запускаться. Хеш, .env, база данных и заявки не должны попадать в GitHub.
Используемую позицию нельзя удалить, пока она входит в состав. Базовый вариант нельзя отключить/удалить до выбора замены. Скрытие элемента также требует убрать его из активных составов. Между несколькими вкладками действует защита от перезаписи старого черновика: при конфликте нужно обновить страницу; несохранённые изменения автоматически не объединяются.
Для лака нужно вручную согласовать число слоёв в его описании, расход на весь цикл и название/цену работы в общем составе. Конструктор намеренно не делает сложный технологический подбор и не проверяет химическую совместимость продуктов.
GitHub — хранилище кода. GitHub Pages не подходит: здесь есть Node-сервер, авторизация и база данных.
Потребуется серверный хостинг с:
Архитектура рассчитана на один экземпляр Node-сервера, не на serverless и не на несколько реплик с отдельными дисками.
Публикуйте репозиторий как private. Ни секреты, ни SQLite не загружайте. Репозиторий может содержать только исходники, seed с демонстрационными ценами и документацию.
На сервере задайте переменные окружения:
NODE_ENV=productionHOST=0.0.0.0 (в контейнере / за reverse proxy)PORT=3000 (или порт хостинга)PUBLIC_ORIGIN=https://ваш-домен — без завершающего слеша и без путиDATA_DIR=/путь/к/постоянному/дискуADMIN_PASSWORD_HASH — значение из созданного локально .env, через секреты хостингаLEADS_ENABLED=false до готовности документовPRIVACY_OPERATOR_CONFIRMED=no до проверки документовTRUST_PROXY=false по умолчанию. Включайте только если доверенный proxy перезаписывает X-Forwarded-For, а прямой доступ к Node закрыт. Иначе посетитель сможет подменять IP для ограничителя запросов.Start: npm start, healthcheck: /health. Для Docker: docker build -t sk-parket .; передайте переменные через секреты хостинга и смонтируйте постоянный диск в /app/data (доступен пользователю node, UID 1000). Не копируйте .env внутрь образа.
Смена домена требует обновления PUBLIC_ORIGIN, сертификата и перезапуска. Только HTTPS в production: cookie сессии становится Secure. За proxy используйте HTTPS termination и закройте внешний доступ к порту Node.
/public/privacy.html и /public/consent.html реальными согласованными документами с данными оператора и сроками хранения. Поставить собственную версию согласия в server.mjs вместо policy-v1 при юридически значимом изменении текста.LEADS_ENABLED=true и PRIVACY_OPERATOR_CONFIRMED=yes только после этих действий. Сервер потребует второе подтверждение; наличие флага само по себе не доказывает готовность документов.До включения сбора форма блокируется, API отвечает отказом. Встроенные проверки не заменяют независимый аудит безопасности перед коммерческим запуском.
.env, исходник сервера и база недоступны через HTTP.Согласованный бэкап SQLite: остановите сервер и копируйте всю DATA_DIR, включая возможные -wal/-shm; либо используйте штатный SQLite online backup. Не копируйте только основной файл во время записи. Храните бэкап отдельно и ограничьте доступ. Проверьте восстановление до запуска.
Для смены пароля повторите npm run setup в безопасном терминале, обновите хеш в секретах хостинга и перезапустите сервер. Для принудительного выхода всех сессий перед перезапуском удалите записи из таблицы sessions через закрытый доступ к базе. Сама смена хеша не удаляет ранее выданные сессии.
npm test
Временная база, случайный тестовый пароль, проверка расчёта, сценариев, авторизации, Origin/CSRF, черновика и публикации, конфликта версий, сохранения заявок и снимков сметы, приватности фото, удаления и сохранности после перезапуска. Реальные данные не нужны.
Опционально node test/ui.cjs с установленными Playwright и Chromium (путь к Chromium при необходимости заменить): проверка сайта и админки на разных ширинах, публикации, добавления паркета, переименования основания и отправки тестовой заявки. Выходные изображения пишутся в /data/output, адаптируйте путь на своём компьютере. Browser-test инструменты не нужны для запуска самого сайта.
server.mjs — HTTP, авторизация, SQLite, API.public/engine.js — валидация состава и общий расчёт.public/site.js, public/index.html — сайт.public/admin.* — админка.config.seed.json — начальные демонстрационные данные; импортируются только при первом запуске пустой базы. Изменение файла после создания базы не меняет опубликованный прайс — для этого админка.scripts/setup.mjs — установка пароля.test/ — временные автоматические проверки.Отдельная статическая сборка находится в docs/. Сборка: npm run build:demo.
Pages: Settings → Pages → Deploy from a branch → main → /docs.
GitHub Pages может требовать платный тариф для private-репозитория; не меняйте видимость репозитория без согласия владельца.
Это не серверная версия: нет настоящего входа, общей базы, отправки заявок или синхронизации между устройствами. Данные сохраняются в localStorage текущего браузера, включая тестовые фото. Не вводите реальные персональные данные. Очищение хранилища/смена устройства удалит доступ к локальным изменениям. Каждый посетитель видит свой независимый набор цен, если менял их в демо-админке. Главный экран админки — заявки; технические кнопки экспорта, импорта и сброса скрыты. История демо хранит последние 20 публикаций. Если место в localStorage закончилось, сохранение выдаст ошибку — удалите тестовые заявки/фото.
Настоящая версия в public/ и server.mjs сохранена отдельно и не использует демонстрационный обход API. Перед рекламным запуском используйте серверную архитектуру, пройдите проверки и оформите документы.
В карточке и подробностях заявки есть «Скачать Excel». XLSX содержит контакты, сохранённую при обращении смету, единицы и количество. Жёлтые ячейки количества и цен редактируются; суммы и итог — формулы. Файл можно импортировать в Google Таблицы. CSV — дополнительная копия со статическими суммами. Экспорт не меняет цены на сайте.
В сборке GitHub Pages номер телефона доступен для повторного ввода, но заявка сохраняется только в текущем браузере и не передаётся мастеру. Уведомление об этом находится возле формы. Это не заменяет серверную базу и защищённую админку.
Клей, шпаклёвочный состав и лак отображаются в админке с ценой за м². Ввод новой цены в таблице переключает расчёт на площадь × цену за м². В редакторе можно вместо этого выбрать расчёт по упаковкам: цена упаковки ÷ её объём × расход на м²; закупка округляется до целых упаковок. Расход лака задаётся на все три слоя суммарно. Старые данные совместимы, суммы сохранённых заявок не пересчитываются.
Excel: сверху только дата заявки и телефон; в смете одна средняя цена (арифметическая середина сохранённого диапазона) и одна сумма. Количество и цена редактируемы, суммы и итог пересчитываются формулами. CSV содержит те же колонки без формул.
При входе в раздел открываются актуальные заявки; поиск работает при вводе и кнопкой «Найти». Excel/CSV доступны внутри заявки. Вверху подробностей выделены телефон, дата/время, стоимость, площадь, выбранные материалы и комментарий. Логотип перезагружает админку. После успешной заявки форма заменяется одним сообщением и восстанавливается при повторном открытии. Серверная версия подтверждает отправку только после успешного API-ответа. Статическая версия честно сообщает о локальном сохранении без отправки мастеру.
Имя необязательно, сохраняется в заявке и доступно в списке, подробностях и выгрузке; старые заявки совместимы. SQLite автоматически добавляет колонку имени в старую базу без удаления данных. Заметка мастера видна на плитке и участвует в поиске. Цена черновика находится в самом поле, без дублирующей подписи. Excel/CSV разделены на «Материалы» и «Работы», неизвестные позиции старых заявок — «Прочие позиции». Промежуточные итоги не удваивают общий итог; цены — середина сохранённого диапазона.
Браузер: кнопка «Включить уведомления» на странице заявок. Разрешение запрашивается только после нажатия. Админка проверяет новые ID каждые 30 секунд; исторические заявки при открытии не вызывают уведомления. Контакты в системное уведомление не выводятся. Для уведомлений админка должна оставаться открытой, в фоне браузер может замедлять таймер. Это не Web Push: после закрытия страницы уведомлений нет. GitHub Pages уведомляет только о локальных заявках своего браузера.
Telegram: поддержан только серверной версией. На сервере задайте TELEGRAM_BOT_TOKEN и TELEGRAM_CHAT_ID через секреты окружения (НЕ в GitHub и не в .env.example). Параметры по умолчанию пустые, интеграция выключена. При создании заявки задача попадает в SQLite-очередь в одной транзакции с заявкой; сервер отправляет контакт, имя, комментарий и краткий расчёт. До 5 попыток с задержкой; ошибки доставки не отменяют сохранение заявки, токен в журнал не выводится. Очередь переживает перезапуск сервера. Для личного чата сначала откройте бота и нажмите Start; нужен числовой ID чата получателя. Перед реальными заявками учитывайте передачу персональных данных в Telegram в документах и согласиях.
Админка слушает изменения локальных данных из других вкладок, события текущей вкладки, возврат фокуса и видимости. Карточки и счётчики обновляются без перезагрузки страницы; фокус в поиске не блокирует получение заявок, строка поиска сохраняется. Опрос сервера остаётся каждые 30 секунд. Excel использует числовой денежный формат рублей с локалью ru-RU (419), включая цены, суммы и итог, без преобразования значений в текст.
Первый экран содержит единые краткие преимущества; подробные условия трёхлетней гарантии на качество работ находятся в нижнем блоке согласований. Обычный износ покрытия отделён от дефектов нанесения. Галерея объектов — ручная карусель со свайпом и открытием пары фотографий нажатием; до получения реальных снимков сохранены явные заглушки. В смете укладка предшествует шлифовке, шпаклеванию и лакировке. Вступление формы без локального предупреждения; на GitHub Pages сообщение после сохранения по-прежнему честно сообщает об отсутствии отправки мастеру.
Пункт «Порядок заказа» заменён на «Как мы работаем»; остальные пункты и заголовок первого раздела сохранены. Убрана дублирующая кнопка звонка в контактах. Общие обещания и FAQ согласованы со сметой и гарантией. Суммы остаются точными до рубля; при закупке целыми упаковками показывается пояснение. В админке: сгруппированная навигация, поиск при вводе без отдельной кнопки, короткие инструкции, удалено неиспользуемое поле пояснения о ценах. Быстрое изменение цены за м² сохраняет текущий способ расчёта: в режиме упаковок пересчитывается цена упаковки по прежнему расходу, а не включается режим расчёта по площади.
Меню начинается с калькулятора и содержит отдельные якоря гарантии и частых вопросов; блок «Что входит» сохранён на странице, но исключён из меню. Рядом с фотографией указан Сергей Королёв — руководитель бригады; его роль отражена и в FAQ. Телефон в шапке уменьшен, на мобильных меню доступно с первого экрана. Категории админки визуально отделены от кнопок, активный раздел выделен контрастно и помечен aria-current.
Браузерные уведомления используют Service Worker (notifications-sw.js) с запасным оконным API. Кнопка проверки позволяет отдельно проверить разрешения браузера и устройства. Оповещения о заявках требуют открытой админки; закрытая страница фоновые push-уведомления не получает. Ключ дедупликации ограничен путём приложения и сбрасывается при обнаружении нового набора данных с меньшими ID.
GitHub Pages по-прежнему хранит заявки отдельно в каждом браузере: Service Worker не создаёт общей базы и не отправляет их в Telegram. Реальная доставка в Telegram выполняется только размещённой серверной частью через защищённые переменные окружения; токен и получатель не добавляются в публичный код.