Chulakov Dev – Telegram
Chulakov Dev
1.15K subscribers
140 photos
5 videos
205 links
Канал команды разработки Студии Олега Чулакова.

Советы по Frontend- и Backend-разработке web-сервисов, мобильных приложений, статьи и презентации от наших разработчиков, анонсы проектов и многое другое.

Обсудить проект @YuraAndreev
Download Telegram
Выполнять!

Что делать, если нужно выполнить php-скрипт, содержащий команду, которая запрещена в конфигурации? Даже если есть доступ к php.ini, включение функций вроде exec может привести к угрозам безопасности сервера.

Одним из решений является запуск нужного скрипта в командной строке c добавлением аргумента -d. Значения перечисленных в нем параметров будут заменены указанными в команде. Остальные параметры будут соответствовать php.ini.

Таким образом, чтобы разблокировать для скрипта все php-функции, можно использовать команду:
php -d disable_functions="" noscript.php
​​Давай сам

В этой заметке расскажем, как организовать Docker-архитектуру, которая сама узнает о новых контейнерах с web-приложениями и опубликует их.

Для доступа к приложениям из внешней среды используется контейнер с прокси-сервером на основе nginx. Он является единственным в Docker-окружении доступным внешней среде по портам 80 (http) и 443 (https). В зависимости от запрашиваемого доменного имени nginx проксирует запрос на соответствующий backend-сервис внутри Docker-сети.

Backend-сервис — это контейнер с web-приложением, принимающий запросы на 80-й порт от nginx. Таких контейнеров в окружении может быть много, и их количество будет расти с релизами новых web-сайтов и сервисов.

Автоматизировать деплоймент новых контейнеров с web-приложениями можно с помощью сервиса на основе образа nginx-proxy.

При добавлении нового сайта нужно обновить конфигурацию nginx и сделать reload. Чтобы не делать этого вручную, избежать ошибок и не тратить время специалистов, используем docker-gen, который уже встроен в образ nginx-proxy.

Контейнер nginx-proxy реализует функцию Service Discovery в нашей Docker-сети, реагируя на регистрацию новых или остановку существующих Docker-контейнеров. Управлением сервисами внутри контейнера с nginx и docker-gen занимается менеджер процессов forego.

Для того чтобы nginx-сервис замечал изменения в окружении, необходимо запускать контейнеры с environment-константой VIRTUAL_HOST. Значением этой константы будет доменное имя web-приложения, для которого произойдет автоматическая настройка проксирования http(s)-запросов.
​​Защищайся

Месяц назад мы рассказали про важность SSL-сертификатов для современных сайтов и сервисов.

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

Нам понадобится сервис letsencrypt-nginx-proxy-companion, который является компаньоном к nginx-proxy и уже умеет с ним взаимодействовать.

Контейнер letsencrypt-nginx-proxy-companion автоматически генерирует новые и обновляет существующие сертификаты, а также дает знать об этом nginx-proxy. Таким образом, для web-приложений, требующих SSL, автоматически изменяются конфигурационные файлы для проксирующего nginx-сервера.

Чтобы сгенерировать сертификат, нам необходимо добавить две константы окружения для команды запуска Docker-контейнера:
1. LETSENCRYPT_HOST — список доменных имен, для которых нужно получить SSL-сертификат.
2. LETSENCRYPT_EMAIL — email для регистрации в Let’s Encrypt.
​​Вы не ответили на мой запрос

Политика безопасности браузеров не позволяет делать AJAX-запросы на другие домены без разрешения от сервера, на который эти запросы поступают.

Но если сервер вернет в ответе специальные заголовки, то кросс-доменный запрос выполнится успешно.

Примеры добавления заголовков на стороне различных веб-серверов и фреймворков:
https://bitbucket.org/snippets/OlegChulakovStudio/reEKM5

Если на стороне frontend требуется работа с сессией, то при отправке запроса нужно активировать флаг withCredentials, который позволит работать с cookie:
https://bitbucket.org/snippets/OlegChulakovStudio/ye7pRa
Погоди

Во многих сервисах пользователям приходится заполнять формы с большим количеством полей. И чем их больше, тем больнее терять введенные данные.

Чтобы спасти пользователя от такой досадной неудачи, обычно используют блокировку перехода и открывают модальное окно с вопросом о подтверждении намерений.

Чтобы реализовать это на стеке React, Redux, React Router 4, можно воспользоваться методом block объекта history, который получают из компонента высшего порядка withRouter. Вызвать его нужно в методе жизненного цикла componentWillUnmount.

Используя наработки из заметок про формы и модальные окна в React, мы собрали новое интерактивное демо с кодом решения:
https://codesandbox.io/s/zlp6zwkr3p

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

Для управления конфигурациями на удаленных машинах мы используем Ansible. Чтобы начать его применять, достаточно:
1) установить Ansible на локальной машине;
2) открыть доступ по SSH-ключу на нужные хосты;
3) на декларативном языке в формате YAML описать задачи, которые необходимо выполнить на хостовых машинах.

В Ansible есть много модулей для установки ПО, выполнения консольных команд, управления пользователями, файловой системой и сетевой инфраструктурой. Также есть поддержка Docker для работы с образами, контейнерами и сервисами.

Рассмотрим пример, в котором необходимо установить инфраструктуру для web-окружения: nginx, Apache, PHP, MySQL. У нас имеются Docker-сервисы, описанные в docker-compose:
https://bitbucket.org/snippets/OlegChulakovStudio/Bez6q5

Напишем конфигурацию Ansible, которая позволит запустить эти сервисы на удаленных машинах.

Сначала сформируем задачи:
1) создать на удаленной машине директорию, в которую мы поместим наш файл docker-compose.yml;
2) скопировать в эту директорию файл docker-compose.yml;
3) запустить сервисы из docker-compose.

Получим такую конфигурацию:
https://bitbucket.org/snippets/OlegChulakovStudio/ke7KBb

Для ее запуска необходимо выполнить команду:
ansible-playbook ./install.yml -i example.host.com
Давай плавнее

Все, кто занимается созданием анимаций, знают про функции плавности (easing) и помнят, как работают основные.

Иногда приходится делать необычные анимации, которых не добиться популярными функциями плавности. Взять, к примеру, появление заголовка на главной странице bakucitycircuit.com — каждый символ заголовка появляется из прозрачности с эффектом включения люминесцентной лампы.

Эту задачу мы решили с помощью GSAP и плагина CustomEase. В коде эта анимация выглядит как обычный fromTo от opacity:0 до opacity:1. Но для нее мы создали в конструкторе специальный easing, вычисленный в процессе неоднократного включения и выключения ламп в офисе Студии.
Не с той стороны

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

Стандартное поведение в MySQL: если мы делаем order by ASC, то строки с null-значениями сортируемого поля помещаются в начале выборки, если же делаем order by DESC — в конце.

Предположим, у нас есть таблица example со следующей структурой и содержимым:
| id | price |
| 1 | 5 |
| 2 | 2 |
| 3 | null |
| 4 | 1 |


1. Попробуем отсортировать данные в порядке возрастания цены, при этом переместить строки с null-значениями в конец.

Обычная сортировка вернет null-значения в начале выборки:
SELECT * FROM example order by price ASC

| id | price |
| 3 | null |
| 4 | 1 |
| 2 | 2 |
| 1 | 5 |


Для того чтобы изменить такое поведение сортировки, можно воспользоваться одним из трюков:
SELECT * FROM example order by -price DESC
SELECT * FROM example order by ISNULL(price), price ASC

| id | price |
| 4 | 1 |
| 2 | 2 |
| 1 | 5 |
| 3 | null |


2. Другая задача: нужно отсортировать выборку в порядке убывания цены, а строки с null-значениями разместить в начале.

По умолчанию после сортировки строки с null-значениями цен будут располагаться в конце выборки:
SELECT * FROM example order by price DESC

| id | price |
| 1 | 5 |
| 2 | 2 |
| 4 | 1 |
| 3 | null |


Для того чтобы получить нужный порядок, можно воспользоваться одним из следующих запросов:
SELECT * FROM example order by -price ASC
SELECT * FROM example order by ISNULL(price) DESC, price DESC

| id | price |
| 3 | null |
| 1 | 5 |
| 2 | 2 |
| 4 | 1 |


Следует заметить, что при сортировке с помощью конструкции order by -price индекс по полю price (если он есть) не будет задействован, тем самым время выборки может возрасти, а оптимальность плана выполнения запроса — снизиться.
Я тебя запомнил

Чтобы ускорить загрузку web-страниц и сэкономить трафик, можно заставить браузер их кэшировать. Для этого необходимо правильно настроить заголовки ответа со стороны web-сервера.

Для этих целей предусмотрены заголовки Cache-Control, If-None-Match, If-Modified-Since, Last-Modified, ETag и другие. Также существует специальный HTTP-статус ответа 304 Not Modified.

Допустим, браузер выполняет GET-запрос страницы chulakov.ru первый раз. Web-сервер формирует и возвращает контент страницы, в ответе отправляет заголовок Last-Modified: Sat, 13 Jan 2018 13:38:04 GMT, который содержит последнюю дату изменения запрашиваемого содержимого страницы.

При повторном просмотре этой страницы браузер включит в запрос заголовок If-Modified-Since: Sat, 13 Jan 2018 13:38:04 GMT. И если контент запрашиваемой страницы не был изменен после переданной даты, то сервер вернет HTTP-статус 304. Это послужит командой для браузера поднять страницу из кэша.

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

Кэширование контента web-страниц применимо к HTTP-запросам, не меняющим данные на сервере. Например, к GET-запросам.
Как вам будет удобно

Поддержка брендов часто включает в себя верстку писем, которые компании рассылают своим клиентам. При разработке сервисов могут потребоваться различные шаблоны email-оповещений. Но верстать письма никто не любит: в популярных почтовых клиентах не работает большинство CSS-свойств, а оставшиеся нужно писать непосредственно в атрибуте style у HTML-элементов. Такой код достаточно быстро становится нечитаемым. Соответственно, дорабатывать верстку письма будет значительно сложнее.

Любой неудобный процесс нужно автоматизировать. Для комфортной верстки писем мы сделали небольшой шаблон, использующий gulp-inline-css. С его помощью можно писать HTML и CSS раздельно, а HTML-файл со всеми стилями получать автоматически. Кроме того, чтобы написание CSS было максимально удобным, мы добавили в сборку препроцессор Stylus, который используем в большинстве проектов.

Таким образом, верстку писем можно сделать такой же привычной, как верстку сайта.
По-моему, я тут уже был

Недавно мы писали про кэширование контента web-страниц, а сегодня поговорим о кэшировании статических файлов: JS, CSS, изображений, шрифтов и т.д.

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

Для того чтобы браузер закэшировал файл на необходимое время, используются заголовки Cache-control, Expires и Vary, которые сервер может прислать вместе с файлом.

Чтобы сбросить кэш браузера для этих файлов, нужно изменить URL-адрес либо с помощью GET-параметра (например, /js/noscript.js?v=2), либо изменив путь до самого файла, как это делает asset-менеджер Yii 2.

Настроить кэширование статики несложно. Вот, например, конфигурация для nginx:
https://bitbucket.org/snippets/OlegChulakovStudio/qeLrEg
А так можно?

Недавно мы рассказали о нашем подходе к работе с CSS при верстке писем. Некоторые подписчики написали нам на ask@deep.digital вопросы о том, что можно использовать в письмах, а что — нет.

Если сделать акцент на том, что чаще всего используется верстальщиками или блокируется популярными почтовыми сервисами и программами, то можем дать следующие рекомендации.

Не использовать float и flex. Если нужно расположить элементы в строку, то, чтобы работало в Outlook, придется делать это с помощью таблиц.

Не использовать position, transform и отрицательные margin. Многие почтовые сервисы удаляют такие свойства, защищаясь от того, что контент письма может «залезть» на их интерфейс.

Во многих почтовых клиентах не работает и background-image. Это исправляется с помощью различных «костылей», но лучше к ним не прибегать и добавлять изображения с помощью тэга img.

Не подключать SVG-изображения, шрифты, JS- и CSS-файлы. Если в тексте есть, например, заголовок, написанный нестандартным шрифтом, то лучше вставить его картинкой.

Не пользоваться в теле письма тэгами, кроме div, span, a, table, tr, td, span, strong/b, em/i и img. Например, применение тэга p может привести к конфликту со стандартными стилями отступов этого элемента у почтового сервиса, а тэги вроде video, audio, form, input будут удалены или не будут выполнять свои функции.
Все течет, все меняется

Кэширование на стороне браузера применимо и к динамическому контенту. Когда загружаемые данные web-страницы изменятся, кэш будет сброшен.

В одной из предыдущих заметок мы рассмотрели кэширование с применением заголовков Last-Modified и If-Modified-Since из спецификации HTTP 1.0.

В спецификации HTTP 1.1 появились более универсальные заголовки Etag и If-None-Match.
При первом запросе контента web-страницы браузеру возвращается заголовок Etag с уникальным идентификатором ресурса в виде хеша. При последующих запросах этого же ресурса серверу передается уникальный хеш с помощью заголовка If-None-Match. Если контент ресурса не поменялся, то сервер вернет HTTP-статус 304 Not Modified и браузер отдаст контент из своего кэша. В противном случае сервер вернет обновленный контент со статусом 200 OK.

Вместе с заголовкамиEtag можно использовать заголовок Expires, в котором задается время максимального хранения кэша.
Следите за выражениями

При составлении регулярных выражений мы часто пользуемся специальными сервисами и только потом переносим эти выражения в свой код.

Иногда даже проверенные регулярные выражения могут работать не так, как ожидалось. Конечно, большинство проблем решается с помощью применения флагов или дополнительного изучения справочной информации.
Но бывают случаи, когда локальные тесты проходят успешно, а при переносе на промышленные серверы поведение «регулярки» меняется не в лучшую сторону.

Работа с кириллицей может стать корнем проблем. Вот такое регулярное выражение отказалось работать с русскими буквами на промышленном сервере заказчика:
$string = preg_replace('/[^\d\w\s\.\:-]+/u', '', $string);


Изучив проблему, мы поняли, что в старых версиях PHP-модуля pcre стандартный перечень \w не охватывает кириллические символы даже с модификатором u. Именно этот модуль реализует работу Perl-совместимых регулярных выражений в PHP.

Для того чтобы ваши регулярные выражения корректно работали с русскими символами, необходимо, чтобы версия pcre была не ниже 8.38. Проверить это можно, выполнив консольную команду
$ php -i | grep PCRE
Разметка? Нет, не слышали

Телеграм дает много возможностей для создания ботов. Часто их используют для публикации новостей в каналах. Публиковать свои сообщения очень просто: метод sendMessage принимает текст в трех форматах — text, markdown и html. А вот парсинг и отображение сообщения (например, на сайте) имеют одну большую проблему — получаемый из Телеграма текст не содержит разметки. В нем никак не обозначены ссылки, курсив и т.д.

Однако вместе с ним передается список объектов MessageEntity, в которых хранится тип, позиция и длины размеченных частей текста. Мы не нашли библиотек, которые могли бы формировать на основе этих данных, например, markdown-код. При этом любое внесение изменений в исходный текст приводит к несовпадению индексов у последующих MessageEntity. Если начать вставлять теги разметки, то текст начнет смещаться с позиций, указанных в MessageEntity, и будет размечаться не тот контент.

Как мы решили проблему?

Шаг 1: Собрать необходимые данные
Мы собрали все части в один список. В него попали входные и закрывающие индексы.
https://bitbucket.org/snippets/OlegChulakovStudio/Beok58

Шаг 2: Разрезать на части!
По существующим индексам мы порезали сообщение на части, начиная с конца. В результате получили маркированный список отрезков исходного текста.
https://bitbucket.org/snippets/OlegChulakovStudio/pey7p6

Шаг 3: Разметить
Имея маркеры и кусочки текста, можно легко преобразовать их с учетом любой верстки и смещений.
https://bitbucket.org/snippets/OlegChulakovStudio/9ekL9j

Шаг 4: Склеить
Теперь текст можно собрать обратно, уже с внесенными изменениями.
$text = implode('', $parts);
Движение вбок

Один из наших подписчиков решил наглядно показать принципы работы современных библиотек для DOM-анимаций. Для этого он написал простую библиотеку и попросил нас ею поделиться:
codepen.io/OlegChulakovStudio/pen/JpYWXX

Так, с ее помощью можно переместить блок по диагонали:
animation($block, .3, 
{
transform: "translate3d({0}px, {0}px, 0px)"
},
{
transform: "translate3d({50}px, {50}px, 0px)"
}
);


Несмотря на небольшой размер, здесь предусмотрена даже поддержка vw, % и т.д. Если вам интересна тема анимаций, рекомендуем поизучать код.

Напоминаем, что интересные идеи или вопросы вы можете присылать нам на ask@deep.digital.
Вы знаете, с какой скоростью вы ехали?

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

Сперва, разумеется, вам потребуется сделать несколько версий медиафайла разного веса/качества. Затем нужно определить скорость скачивания и на ее основе отобразить подходящий вариант.

Готовится спецификация Network Information API, которая позволит определить параметры сети, но сейчас ее еще рано использовать в production-среде.

В суровом мире кроссбраузерности порой приходится прибегать к менее изысканным решениям. Для этой задачи наиболее популярным является замер времени скачивания заранее известного объема данных (например, изображения):
https://bitbucket.org/snippets/OlegChulakovStudio/6ey8MR

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

Нам на ask@deep.digital пришел вопрос: «Как сервисы почтовой рассылки вроде MailChimp собирают статистику о прочтении писем?» Рассказываем.

Перед отправкой в HTML-код письма автоматически добавляется тэг img. Он ссылается на маленькое прозрачное GIF- или PNG-изображение. Перед отправкой backend генерирует уникальный URL этого изображения для каждого адресата. Когда пользователь откроет письмо в своем почтовом клиенте, изображение начнет скачиваться с сервера, этот запрос будет зафиксирован и факт открытия письма адресатом запишется в базу данных.

Разумеется, точность такой статистики снижается из-за блокировки изображений в зависимости от настроек почтовых клиентов у пользователей и политик безопасности используемых ими сервисов.

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

При разработке сайтов важно не забывать о том, что у посетителей могут быть заблокированы API встраиваемых сервисов. Например, виджетов VK или Facebook, скриптов Google Analytics. Если не учесть эти блокировки, то от части пользователей будут поступать сообщения о том, что в работе сайта есть ошибки.

Чаще всего причиной являются установленные в браузере AdBlock-плагины, но также проблемы бывают связаны с ограничениями доступа (например, у пользователей из Украины сейчас полностью заблокирован VK).

Что делать?

Тестировщику нужно настроить как минимум один браузер на абсолютно беспощадный режим блокировки: установить AdBlock, Ghostery и другие подобные плагины. Включить в них все преднастроенные ограничения, полностью заблокировать обращения к доменам социальных сетей и популярных сервисов.

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

Например, если вы разместили на сайте кнопку Like, а Facebook JS SDK у пользователя не скачался, то он не должен об этом узнать: верстка не должна поехать, а функционал сайта — сломаться.
Социальное чтиво

Когда пользователь делится в соцсети ссылкой, то в ленту добавляется виджет с заголовком соответствующей страницы, ее описанием и изображением.

Эти данные VK и Facebook получают, посылая запрос на страницу и анализируя ее HTML-код. Если разработчик хочет добиться предсказуемого поведения и контролировать содержимое виджетов, то ему необходимо использовать специальные метатеги.

Например, для указания изображения используется метатег <meta property="og:image" content="URL" />. Если его не разместить, то картинки в виджете либо не будет вообще, либо краулер соцсети сам возьмет первую подходящую из кода страницы. А это может быть что угодно, даже какой-нибудь рекламный баннер, не имеющий отношения к содержимому, которым делился пользователь.

В метатеге og:image нужно указывать абсолютный URL изображения с протоколом и доменом. Чтобы картинка загрузилась, при самом первом шейре страницы нужно добавить метатеги og:image:width и og:image:height с соответствующими размерами изображения.
Все по-старому

В прошлой заметке мы говорили про публикацию ссылок в соцсетях и про то, что VK и Facebook получают метаданные страницы, посылая на нее запрос и анализируя HTML-код.

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

Если вы обновили og:image или другие поля, то, для того чтобы пользователи начали ими делиться, нужно сбросить кэш для страницы. Для этого можно использовать специальные инструменты или методы API:
https://developers.facebook.com/tools/debug
https://vk.com/dev/pages.clearCache