← Все проекты

Commercial Projects / автомобильная шумоизоляция

Shumka61

Сайт мастерской, где портфолио работ, управление фотографиями и приём обращений связаны в одну систему.

Продукт
Сайт действующего бизнеса
Разработка
Frontend, backend, админ-панель, медиа и деплой
Стек
Vue 3 · Vite · PHP · MySQL · Apache

Задача бизнеса

Для мастерской автомобильной шумоизоляции важно показать качество работ до первого звонка. Посетителю нужны примеры автомобилей, фотографии процесса, информация об услугах и простой способ оставить заявку.

Владельцу нужен другой сценарий: самостоятельно добавлять работы и фотографии, поддерживать актуальность сайта и получать обращения. Поэтому проект включает публичный сайт и отдельную систему управления контентом.

Роль разработчика

Я сделал проект полностью самостоятельно: дизайн, Vue-интерфейс, PHP API, модель данных MySQL, админ-панель, обработку фотографий, уведомления и развёртывание на Apache.

Отдельного макета в Figma не было: дизайн собирался и перерабатывался непосредственно в коде. Это позволило сразу проверять интерфейс в браузере, но требовало следить за согласованностью компонентов по мере развития сайта.

Архитектура

Публичная часть и админ-панель решают разные задачи, но используют общую модель контента. Исходные загрузки, опубликованные изображения и данные заявок имеют разные правила хранения и доступа.

  1. 01 · ИнтерфейсVue 3 + Vite: страницы, карточки работ, галерея, форма заявки.
  2. 02 · СерверPHP API: проверка данных, операции с контентом, обработка изображений.
  3. 03 · ХранениеMySQL: работы, фотографии, заявки и очереди. Файлы изображений хранятся отдельно.

Форма → проверка и защита от повторов → транзакция MySQL → заявка и задания уведомлений → обработчик доставки → Telegram и email. Доставка уведомления не определяет, сохранена ли заявка.

В production используются Apache и PHP-FPM. Конфигурация, медиа и постоянные данные отделены от каталога релиза. Для обновления кода переключается ссылка на новый релиз.

Frontend: от просмотра к обращению

На сайте есть главная, портфолио, отзывы и контакты. Основной сценарий: изучить услуги, найти похожий автомобиль, посмотреть работу и оставить заявку.

Каталог сначала запрашивает краткие сведения об автомобилях и обложки. Фотографии конкретной работы загружаются при её открытии; полноразмерные изображения — при просмотре. Это позволяет не скачивать сразу весь фотоархив мастерской.

Галерея учитывает ошибки загрузки, навигацию с клавиатуры и возврат фокуса после закрытия. Предзагрузка ограничена соседними фотографиями.

Shumka61: главная страница с услугами и навигацией
Реальный публичный сайт, снимок от 7 октября 2026. Нажмите, чтобы открыть изображение.
Shumka61: каталог выполненных работ
Каталог работ: фотографии — часть доказательства качества услуги, а не декоративный фон.

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.

Результат. Выкладка кода не требует переносить секреты и фотоархив; откат кода возможен при совместимой схеме базы.

Работающий результат и ограничения

Результат — опубликованный сайт бизнеса с управляемым портфолио, серверной обработкой фотографий и приёмом обращений. Посетитель может посмотреть реальные работы, а владелец — обновлять материалы через админ-панель.

В проекте предусмотрены проверки интеграции админ-панели, миграций, медиа и восстановления резервной копии. Сама доступность страницы не доказывает исправность всех этих сценариев: для них нужны отдельные проверки.

Без выдуманных метрик. Нет подтверждённых данных о росте продаж, конверсии или скорости обработки обращений, поэтому кейс не приписывает сайту такие результаты. Показаны реализованные функции и проверяемые технические решения.

Что я бы развивал дальше

Собирать измерения основных пользовательских сценариев, наблюдать за задержкой доставки уведомлений и регулярно проверять восстановление данных. Затем улучшать продукт по результатам этих проверок, а не по одному показателю скорости загрузки.