Commercial Projects / автомобильная шумоизоляция
Shumka61
Сайт мастерской, где портфолио работ, управление фотографиями и приём обращений связаны в одну систему.
- Продукт
- Сайт действующего бизнеса
- Разработка
- Frontend, backend, админ-панель, медиа и деплой
- Стек
- Vue 3 · Vite · PHP · MySQL · Apache
Задача бизнеса
Для мастерской автомобильной шумоизоляции важно показать качество работ до первого звонка. Посетителю нужны примеры автомобилей, фотографии процесса, информация об услугах и простой способ оставить заявку.
Владельцу нужен другой сценарий: самостоятельно добавлять работы и фотографии, поддерживать актуальность сайта и получать обращения. Поэтому проект включает публичный сайт и отдельную систему управления контентом.
Роль разработчика
Я сделал проект полностью самостоятельно: дизайн, Vue-интерфейс, PHP API, модель данных MySQL, админ-панель, обработку фотографий, уведомления и развёртывание на Apache.
Отдельного макета в Figma не было: дизайн собирался и перерабатывался непосредственно в коде. Это позволило сразу проверять интерфейс в браузере, но требовало следить за согласованностью компонентов по мере развития сайта.
Архитектура
Публичная часть и админ-панель решают разные задачи, но используют общую модель контента. Исходные загрузки, опубликованные изображения и данные заявок имеют разные правила хранения и доступа.
- 01 · ИнтерфейсVue 3 + Vite: страницы, карточки работ, галерея, форма заявки.
- 02 · СерверPHP API: проверка данных, операции с контентом, обработка изображений.
- 03 · ХранениеMySQL: работы, фотографии, заявки и очереди. Файлы изображений хранятся отдельно.
Форма → проверка и защита от повторов → транзакция MySQL → заявка и задания уведомлений → обработчик доставки → Telegram и email. Доставка уведомления не определяет, сохранена ли заявка.
В production используются Apache и PHP-FPM. Конфигурация, медиа и постоянные данные отделены от каталога релиза. Для обновления кода переключается ссылка на новый релиз.
Frontend: от просмотра к обращению
На сайте есть главная, портфолио, отзывы и контакты. Основной сценарий: изучить услуги, найти похожий автомобиль, посмотреть работу и оставить заявку.
Каталог сначала запрашивает краткие сведения об автомобилях и обложки. Фотографии конкретной работы загружаются при её открытии; полноразмерные изображения — при просмотре. Это позволяет не скачивать сразу весь фотоархив мастерской.
Галерея учитывает ошибки загрузки, навигацию с клавиатуры и возврат фокуса после закрытия. Предзагрузка ограничена соседними фотографиями.


Backend и админ-панель
PHP API выдаёт публичный контент и отдельно обслуживает административные операции. Работа с MySQL идёт через PDO и подготовленные запросы; изменения, которые должны произойти вместе, объединяются в транзакции.
Админ-панель позволяет управлять автомобилями и фотографиями, менять порядок изображений, поворачивать и удалять их, работать с сертификатами и заявками. Для просмотра журнала безопасности предусмотрена отдельная страница.
Публичная часть получает данные из базы: для обновления портфолио не требуется пересобирать frontend. Административные действия проверяются на сервере, а не только скрываются в интерфейсе.
Фотографии: загрузка и жизненный цикл
Фотографии приходят с разных устройств: большой размер, разная ориентация, разные форматы. Сервер декодирует JPEG, PNG и WebP, применяет EXIF-ориентацию и создаёт два производных WebP.
- Основное изображение: вписывание в 1920 × 1080 без увеличения маленьких файлов.
- Миниатюра портфолио: центральное кадрирование 400 × 300; для сертификатов — вписывание на белый фон.
- Ограничения обработки: до 25 МБ и 24 мегапикселей на файл, до 20 файлов в одном запросе.
Новый файл сначала полностью записывается во временное место и только затем публикуется. При повороте создаются новые имена: браузер не показывает старую версию из кеша. Исходный multipart-файл находится вне публичного каталога и удаляется после обработки.
В базе есть реестр медиа и вариантов с размерами, MIME, объёмом и контрольной суммой. При удалении записи создаётся задание очистки файлов: неудачную очистку можно повторить, не восстанавливая удалённую запись.
Заявки и Telegram-уведомления
API проверяет поля, нормализует ввод и сохраняет заявку вместе с заданиями отправки в одной транзакции. У каждой повторяемой отправки есть идентификатор запроса: двойной клик или повтор после сетевой ошибки не должен создавать новую заявку.
Отдельный обработчик отправляет уведомления в Telegram и по SMTP. Telegram может использовать SOCKS-прокси с разрешением DNS через прокси; TLS-сертификаты проверяются. Неуспешная доставка остаётся задачей очереди, а обращение — записью в базе.
Для ограничения частоты используется атомарный счётчик в MySQL. IP хранится как HMAC с серверным секретом; исходный адрес не нужен для ключа лимита.
Безопасность как часть реализации
- Административные сессии и проверка доступа на сервере, обновление идентификатора сессии после входа.
- CSRF-токены для изменяющих административных запросов, включая выход.
- Проверка загрузок по содержимому и декодированию, ограничения размера и пикселей.
- Невыполняемый каталог медиа и временные оригиналы вне DocumentRoot.
- Секреты и конфигурация окружения вне публичного каталога; отдельная база и ограниченный пользователь приложения.
- Проверка целостности релиза перед публикацией и изоляция постоянного хранилища от кода.
Это конкретные меры в проекте, а не обещание отсутствия любых уязвимостей. Клиентские данные, ключи и внутренние адреса в этот кейс не включены.
Проблемы → решения → результат
Большая галерея не должна загружаться целиком
Проблема. Архив фотографий тяжёлый для посетителя, который ещё не выбрал автомобиль.
Решение. Разделить выдачу кратких карточек и фотографий работы; использовать миниатюры и загрузку по действию пользователя.
Результат. Первый просмотр каталога не требует скачивания всех оригиналов.
Поворот фотографии и кеш браузера
Проблема. Изменение файла под старым URL может оставить у посетителя старую ориентацию.
Решение. Создавать новую пару WebP с уникальными именами и менять ссылки в транзакции.
Результат. URL обозначает конкретную версию изображения, а браузер получает новый файл.
Сбой уведомления не должен терять заявку
Проблема. Telegram или почта могут быть временно недоступны.
Решение. Сохранить заявку и задания доставки в MySQL, отправлять через обработчик очереди.
Результат. Сохранённое обращение существует независимо от доступности внешнего канала.
Удаление данных и файлов может разойтись
Проблема. Файл может не удалиться после удаления записи из базы.
Решение. Создавать задание очистки в той же транзакции и повторять файловую операцию.
Результат. Очистку можно завершить позднее; ошибка файловой системы не возвращает удалённую работу.
Обновление не должно заменять фотографии и конфигурацию
Проблема. Копирование новой версии поверх сайта смешивает код с постоянными данными.
Решение. Разнести релизы и постоянное хранилище; проверять manifest и переключать current.
Результат. Выкладка кода не требует переносить секреты и фотоархив; откат кода возможен при совместимой схеме базы.
Работающий результат и ограничения
Результат — опубликованный сайт бизнеса с управляемым портфолио, серверной обработкой фотографий и приёмом обращений. Посетитель может посмотреть реальные работы, а владелец — обновлять материалы через админ-панель.
В проекте предусмотрены проверки интеграции админ-панели, миграций, медиа и восстановления резервной копии. Сама доступность страницы не доказывает исправность всех этих сценариев: для них нужны отдельные проверки.
Без выдуманных метрик. Нет подтверждённых данных о росте продаж, конверсии или скорости обработки обращений, поэтому кейс не приписывает сайту такие результаты. Показаны реализованные функции и проверяемые технические решения.
Что я бы развивал дальше
Собирать измерения основных пользовательских сценариев, наблюдать за задержкой доставки уведомлений и регулярно проверять восстановление данных. Затем улучшать продукт по результатам этих проверок, а не по одному показателю скорости загрузки.