...

суббота, 13 июня 2020 г.

Functional FizzBuzz на Scala

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

Предлагаю вашему вниманию еще один вариант, не совсем пятничный, а скорее субботний: FizzBuzz на Scala, functional style.


Задача

Для чисел от 1 до 100 нужно выводить на экран


  • Fizz, если число делится на 3;
  • Buzz, если число делится на 5;
  • FizzBuzz, если число делится и на 3 и на 5;
  • в противном случае само число.

Решение


Программист должен не столько решать задачу, сколько создавать инструмент для ее решения

Начнем с делимости

def divisibleBy(n: Int, d: Int): Boolean = n % d == 0

divisibleBy(10, 5) // => true

Нет, это нас не устроит — ведь делимость это свойство не только чисел типа Int, опишем делимость в общем виде, а за одно сделаем ее инфиксным оператором (Тут и далее используются некоторые возможности библиотеки cats):

import cats.implicits._
import cats.Eq

implicit class DivisionSyntax[T](val value: T) extends AnyVal {
  def divisibleBy(n: T)(implicit I: Integral[T], ev: Eq[T]): Boolean = {
    import I._
    (value % n) === zero
  }
  def divisibleByInt(n: Int)(implicit I: Integral[T], ev: Eq[T]): Boolean =
    divisibleBy(I.fromInt(n))
}

10 divisibleBy 5 // => true
BigInt(10) divisibleBy BigInt(3) // => false
BigInt(10) divisibleByInt 3 // => false

Тут используются:


  • type class "Integral" требующий от типа "T" возможности вычислять остаток от деления и иметь значение "zero"
  • type class "Eq" требующий от типа "T" возможности сравнивать его элементы (оператор "===" это его синтаксис)
  • расширение типа "T" с помощью extension methods & value classes, которое не имеет рантайм-оверхеда (ждем dotty, который принесет нам нормальный синтаксис экстеншен методов)

Строго говоря метод divisibleByInt не совсем тут нужен, но он пригодится нам позже, если мы захотим использовать литералы целочисленного типа 3 и 5.


FizzBuzz

Отлично! Перейдем к вычислению того, что нужно вывести на экран, напомню, что это может быть "Fizz", "Buzz", "FizzBuzz" либо само число. Тут есть общий паттерн — некоторое значение участвует в результате, только если выполняется определенное условие. Для этого подойдет Option, который будет определять используется значение или нет:

def useIf[T](value: T, condition: Boolean) = if (condition) Some(value) else None

Как и в случае с "divisibleBy(10, 5)" и "10 divisibleBy 5" задача решается, но как-то некрасиво. Мы ведь хотим не только решить задачу, но и создать инструмент для ее решения, DSL! По-сути, большая часть работы программиста и есть создание DSL разного рода, когда мы отделяем "как сделать" от "что сделать", "10 % 5 == 0" от "10 divisibleBy 5".

implicit class WhenSyntax[T](val value: T) extends AnyVal {
  def when(condition: Boolean): Option[T] = if (condition) Some(value) else None
}

"Fizz" when (6 divisibleBy 3) // => Some("Fizz")
"Buzz" when (6 divisibleBy 5) // => None

Осталось собрать все вместе! Мы могли бы использовать orElse и получили бы 3 правильных ответа из 4, но когда мы должны вывести "FizzBuzz" это не сработает, нам нужно получить Some("Fizz") ? Some("Buzz") => Some("FizzBuzz"). Просто строки можно складывать, но как сложить Option[String]? Тут на помощь нам приходят монады моноиды, cats предоставляет нам все нужные инстансы и даже удобный синтаксис:

  def fizzBuzz[T: Integral: Eq: Show](number: T): String =
    ("Fizz" when (number divisibleByInt 3)) |+|
    ("Buzz" when (number divisibleByInt 5)) getOrElse
    number.show

Тут type class Show дает типу T возможность превращения в строку, |+| синтаксис моноида для сложения и getOrElse задает значение по-умолчанию. Все в общем виде и для любых типов, мы могли бы и от строк "Fizz" & "Buzz" абстрагироваться, но это лишнее на мой взгляд.


Конец

Все, что нам осталось сделать это (1 to 100) map fizzBuzz[Int] и куда-нибудь вывести результат. Но это уже совсем другая история...

Let's block ads! (Why?)

[Из песочницы] Автоматизация квартиры

Предыстория


Давняя мечта об автоматизации квартиры начала свое превращение в реальность с покупки квартиры в новостройке. Уже на этапе планирования ремонта вырисовались основные требования к инженерным сетям:
  1. гибкое управление освещением, водоснабжением, вентиляцией, отоплением и силовыми нагрузками;
  2. возможность реализации сценариев;
  3. удаленное управление и оповещение;
  4. централизованное отключение всего освещения;
  5. централизованное отключение неприоритетных нагрузок и водоснабжения;
  6. в перспективе – возможность голосового управления.

Электрика


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

По проекту застройщика, все нагрузки были поделены на 4 группы:

  • котел;
  • освещение;
  • розетки кухни и коридора;
  • всё остальное.

Вся модульная автоматика была расположена в этажном щите, в квартиру заходили 4 кабеля – 2 3х2,5 мм2 и 2 3х1,5 мм2, что не соответствовало моим потребностям. Было принято решение о перетяжке кабеля от этажного щита и переносе распределительного щита непосредственно в квартиру. В результате в этажном щите остался счетчик и вводной автоматической выключатель на 40А, далее кабель 3х6 мм2 до квартирного щита.

При проектировании квартирного щита учитывались следующие требования:

  • защита от перенапряжений;
  • приоритетная (неотключаемая) нагрузка;
  • неприоритетная (отключаемая) нагрузка;
  • дифференциальная защита на «опасных» группах (все, что эксплуатируется во влажных помещениях, все что связано с водой и все, до чего могут дотянуться дети).

В итоге вырисовалась следующая схема:
image

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

Для размещения всей необходимой модульной автоматики был выбран встраиваемый щит на 72 модуля (как впоследствии выяснилось – места в щите много не бывает).

Начался процесс полной переделки электрики внутри квартиры. Из особенностей – кабельные трассы от всех источников освещения и выключателей были заведены непосредственно в квартирный щит.

Автоматика


В силу того, что в своей практике приходилось сталкиваться с промышленной автоматикой, в качестве устройства управления рассматривались разные варианты ПЛК и ПЛР от Siemens, ABB, Schneider-Electric, Овен и т.д. В итоге выбор пал на программируемое логическое реле ONI PLR (PLR-S-CPU-1004R-AC-BE, далее, для краткости – контроллер). Почему именно так:
  • встроенная шина RS-485 с поддержкой протокола Modbus;
  • дискретные выходы, реализованные в виде 3-х и 10-ти амперных реле;
  • питание от 220В;
  • компактные размеры;
  • форм-фактор для установки на DIN-рейку;
  • наличие экрана и органов управления непосредственно на корпусе;
  • цена.

Так как 10 дискретных входов и 4 релейных выходов было недостаточно, в дополнение к основному блоку, было выбрано два модуля расширения по 8 дискретных входов, 4 релейных выхода 3А, 4 релейных выхода 10А. Разработка под данное реле ведется с использование языка функциональных диаграмм (FBD).

Водоснабжение


Водоснабжение квартиры застройщик реализовал таким образом, что в квартире находятся 3 стояка холодной воды. Т.к. горячая вода готовится в газовом котле, то для полного отключения воды, например в ванной комнате, необходимо перекрыть как стояк в ванной, так и стояк на кухне, от которого питается котел. Исходя из этого, для управления подачей воды и в качестве системы защиты от протечек были приобретены 3 комплекта Гидролок (приводы с автономным питанием и датчики), которые имеют возможности объединения в единую систему, централизованного внешнего управления, а также съема сигнала о срабатывании датчиков протечки. К месту установки каждого привода от квартирного щита был подведен кабель STP.

Установленный шаровый кран с приводом:

image

Выбор ПО управления


Т.к. являюсь пользователем различных гаджетов компании Samsung, в качестве программной платформы для управления решил остановиться на ПО SmartThings, которое поддерживает интеграцию через облачный коннектор. Для стыковки ONI PLR со SmartThings Cloud потребуется разработать OPC-сервер и CloudConnector.

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

В итоге вырисовалась следующая схема взаимодействия компонентов системы:

image

Реализация


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

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

image

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

Подключение выключателей и нагрузок к контроллеру осуществлялось по следующей схеме:

image

Выключатели:

image

В процессе пуско-наладки контроллера и отладки ПО были замечены странные задержки в 0,5 — 1 секунду при включении/отключении релейных выходов, находящихся в модулях расширения. Проблема оказалась весьма банальной – невнимательное изучение мануалов. Перед подключением необходимо было выставить DIP-переключателями адреса для модулей расширения, как только это было сделано, контроллер заработал как часы.

В настоящее время собранный щит в интерьере выглядит следующим образом:

image

На текущий момент реализованы следующие функции:
  • управление освещением с выключателей, для ряда помещений – из нескольких мест;
  • простые сценарии с автоматическим включением вентиляции в помещениях с повышенной влажностью (пока без соответствующих датчиков, только на основании типовых поведенческих сценариев, исходя из времени нахождения людей в помещениях);
  • общее отключение освещения (отдельный выключатель);
  • принудительное отключение водоснабжения;
  • отключение водоснабжения при намокании датчиков.

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

Let's block ads! (Why?)

WSJ: Amazon анализирует продукцию сторонних продавцов и выпускает товары-конкуренты; Евросоюз подаёт в суд

Еврокомиссия планирует подать в суд на компанию Amazon. Корпорацию могут обвинить в сборе данных от сторонних продавцов и использовании этой информации для разработки собственных товаров. Об этом сообщает The Wall Street Journal.

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

Amazon отказалась от комментариев. Ранее компания оспаривала обвинения в злоупотреблении своим влиянием и утверждала, что ритейлеры свободно продают свои собственные торговые марки на платформе. Тем не менее, авторы расследования, опубликованного Wall Street Journal в апреле, пришли к выводу, что сотрудники интернет-магазина иногда использовали данные других продавцов для разработки собственных продуктов компании. После публикации в WSJ один из представителей Конгресса США обратился в Amazon с просьбой подтвердить или опровергнуть факт использования «данных об отдельных продавцах для прямой конкуренции» с другими компаниями на платформе. Amazon начала внутреннее расследование и заявила, что подобное поведение будет являться нарушением её политики.

В прошлом году Юридический комитет Палаты представителей Конгресса США также начал антимонопольное расследование в отношении нескольких технологических компаний, включая Amazon. Помимо этого, корпорация является объектом отдельного антимонопольного расследования со стороны Министерства юстиции и Федеральной торговой комиссии США.

Решение Еврокомиссии о том, нарушает ли Amazon законы о конкуренции, ожидается самое ранее через год. Если регулятор установит факт нарушения законодательства ЕС, комиссия может наложить на Amazon штраф в размере до 10% годового дохода компании и вынудить ее скорректировать методы ведения бизнеса. Amazon, в свою очередь, может попытаться оспорить любое такое решение в суде ЕС.

Ранее Еврокомиссия оштрафовала Google на $9 млрд за нарушение закона о конкуренции. Кроме того,в прошлом году были запущены два расследования в отношении Google и Facebook. Предполагаемые нарушения законов о конкуренции со стороны американских платформ могут вылиться в нормативные изменения. Как пишет WSJ, еврокомиссар по вопросам конкуренции Маргрете Вестагер может выдвинуть ряд предложений к концу года, связанных с деятельностью крупных технологических компаний. Нововведения могут изменить деловую практику доминирующих платформ.

Let's block ads! (Why?)

SpaceX успешно вывела на орбиту еще 58 спутников системы Starlink


Старт Falcon 9, фото из Twitter SpaceX.

13 июня 2020 года в 12.21 мск SpaceX успешно запустила со стартового комплекса LC-40 на космодроме на мысе Канаверал (штат Флорида) ракету-носитель Falcon 9 с 58 телекоммуникационными спутниками системы Starlink. Теперь в составе этой системы связи в околоземном пространстве находятся 538 космических аппаратов.

Также в качестве дополнительной нагрузки на Falcon 9 были выведены на орбиту три космических аппарата SkySat американской компании Planet Labs. На них размещены портативные телескопы и камеры для мониторинга поверхности Земли.
Через 15 минут после старта Falcon 9 группа спутников Starlink, масса каждого из которых составляет 260 кг, отделилась от второй ступени и начала выходить на эллиптическую орбиту высотой от 212 до 386 км. Далее специалисты SpaceX будут проверять их работоспособность. Спутники будут использовать свои ионные двигатели для подъема на рабочую орбиту высотой 550 км.

Многоразовая ступень ракеты-носителя Falcon 9 через 8 минут 42 секунды после запуска совершила успешную посадку на плавучую платформу Of Course I Still Love You в Атлантическом океане в 630 км от космодрома на мысе Канаверал. Это был ее третий рабочий запуск.

Все три спутника Planet Labs также были успешно выведены на свои орбиты.

Данный запуск был девятым по счету с мая 2019 года по выводу на орбиту групп мини-спутников в рамках проекта Starlink для создания полномасштабной сети, которая позволит обеспечить пользователей широкополосным доступом в интернет в любом части планеты. SpaceX собирается в 2020 году обеспечить с помощью текущей спутниковой группировки, которая будет еще пополняться, интернет-покрытием всю площадь Северной Америки. В планах компании к концу 2021 года охватить покрытием системы Starlink почти всю планету.

Ранее 23 апреля 2020 года Илон Маск раскрыл даты запуска системы Starlink для тестирования. Закрытое бета-тестирование начнется примерно в середине лета, а публичное бета-тестирование ожидается примерно через полгода. Причем процесс тестирования начнется в зоне высоких широт. Как ожидает SpaceX, задержка при передачи данных в системе Starlink не превысит 25-35 мс, что намного лучше, чем у современных спутниковых систем, у которых около 600 мс, и достаточно близкий показатель к проводным услугам интернет-доступа. Однако, это будет только между коммутационными точками самой системы, а что будет у пользователей покажет тестирование.


У пользователей сразу возник вопрос про страны, которые могут попасть в это тестирование. Оказалось, что, например, пользователи из Германии смогут принять в нем участие из-за своего расположения.

Let's block ads! (Why?)

Видеозапись в облако своими руками

Идёте вы, уважаемый читатель, погожим летним вечером по улице, никого не трогаете, и тут… на вас наезжают (тьфу-тьфу-тьфу, как говорится). Хулиганы, просто прохожие, специальные товарищи (как пелось в одной старой песенке) — не столь важно. Вы достаете телефон и начинаете снимать происходящее на видео. Это не очень нравится наезжающим, и телефон у вас отбирают (или изымают на законных основаниях — нужное подчеркнуть). Свидетелей нет, видеозаписи на телефоне больше нет, доказательств для полиции и суда тоже, соответственно, никаких.

Выход из этой ситуации очевиден: видеозапись должна вестись не в локальный файл на ваш телефон, а непосредственно на удаленный сервер. Правда, готовых программных решений для реализации этой идеи не так много (например, вот): в большинстве случаев предлагаемые приложения для мобильного телефона или платные, или работают из рук вон плохо. Экзотические рекомендации типа «в случае нападения хулиганов начните трансляцию на YouTube» я не рассматриваю, так как в реальной ситуации у вас элементарно не будет времени, чтобы запустить трансляцию. Кроме того, видео будет писаться в чьё-то чужое облако, а очень часто это не есть хорошо.

Можно, конечно, подучить Java или Kotlin (а заодно и Swift) или, на худой конец, освоить PhoneGap и написать своё приложение. Однако всё гораздо проще: под катом несложное решение этой задачи посредством HTML5 video/audio API.


Связываться ли с WebRTC

Безусловно, WebRTC — очень крутая штука, позволяющая вести трансляцию в облако непосредственно. Однако реализация такой трансляции — тот еще геморрой, поэтому я выбрал решение гораздо проще. Видео пишется в оперативную память телефона (заметьте, не на SD-карту, а только в оперативную память) и каждую минуту (например), а также по завершении записи отправляется на сервер. То есть даже если хулиганы начали отбирать у вас телефон — вы успеваете нажать кнопку «стоп» и последний видеофайл уходит на сервер.

При настройках по умолчанию одна минута записи — это файл размером около 20 МБ. При этом никаких приложений, хоть готовых, хоть самописных — только хардкор, только HTML и javascript.


Проблема кроссбраузерности

Справедливости ради надо сказать, что поддержка HTML5 video/audio API, хоть и развивается стремительно, все еще доставляет массу проблем разработчику. В предлагаемом ниже коде я сознательно не стал приводить кроссбраузерного варианта, чтобы не усложнять восприятие. Я даже, если честно, не тестировал этот код под различными ОС и различными браузерами: всё написанное замечательно работает в Mozilla Firefox 68 из-под Debian и в Chrome 83 из-под Android 7; в Chromium 80 из-под Debian и во многих браузерах для Android уже не работает в том, виде, в котором написано.

Так как вы будете использовать предложенное ниже исключительно в личных целях и на своем (скорее всего, на одном) мобильном телефоне, нужно просто найти реализацию video/audio API, поддерживаемую вашим устройством. Так, использованное мною navigator.mediaDevices.getUserMedia() придется, возможно, заменить на navigator.getUserMedia() или даже на navigator.webkitGetUserMedia, либо на navigator.mozGetUserMedia. Можно, конечно, написать и кроссбраузерный вариант. Кроме того, может потребоваться замена конструкции video.srcObject = stream на video.src = URL.createObjectURL(stream). Наконец, проблемы могут возникнуть из-за отсутствия поддержки MediaRecorder и fetch; последний, впрочем, легко заменяется AJAX'ом.


Итак, приступим… Фронтенд

Как вы уже, наверно, поняли, мы собираемся написать html-страничку, которая берет видеопоток с камеры телефона (или ноутбука, или планшета, или стационарного компьютера) и раз в минуту отправляет соответствующий видеофайл на сервер fetch-запросом.

Html-файл очень прост, если не сказать элементарен:

<!DOCTYPE html>
<html lang="ru">
<head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,
      initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="robots" content="noindex, nofollow">
<link rel="stylesheet" type="text/css" href="style.css">
<title>VideoCamera</title>
</head>
<body>

<video muted></video>
<button type="button" onclick="go()">&#9210;</button>
<script src="main.js"></script>

</body>
</html>

Здесь, собственно, только два элемента: окно, в котором пользователю будет показываться снимаемое им видео (без звука, чтобы не было эффекта эха; при этом на сервер звук будет отправляться, естественно) и кнопка «Запись/Стоп». Для того, чтобы все это красиво выглядело и на телефоне, и на десктопе, пишем нехитрый style.css:

html {
   height: 100%;}
body {
   height: 100%; margin: 0px; padding: 0px; background: black;
   text-align: center;}
video {
   display: block; max-height: 100%; max-width: 100%; margin: auto;}
button {
   display: inline-block; width: 2em; margin-left: -1em;
   position: absolute; bottom: 20px; left: 50%; background: none;
   outline: none; border: none; font-size: 30px;  text-align: center;}

И, наконец, main.js, который выполняет всю работу на фронтенде:

"use strict";

// Длительность одного блока записи в секундах
const recTime = 60;

// Забираем пароль из queryString
let pwd = location.search || 'a'; pwd = pwd.trim().replace('?', '');

const video = document.querySelector("video"),
      butt  = document.querySelector("button");

let media, playFlag = false;

// Начать запись видео
const play = async () => {
   try {
      // Если клиент зашел со смартфона, включаем основную камеру
      let c = /Android|iPhone/i.test(navigator.userAgent) ?
         {video:{facingMode:{exact:"environment"}}, audio:true} :
         {video:true, audio:true};

      // Получаем видеопоток с камеры и показываем его юзеру
      let stream = await navigator.mediaDevices.getUserMedia(c);
      video.srcObject = stream;
      video.play();

      // Пишем видеопоток на сервер каждые recTime секунд
      media = new MediaRecorder(stream);
      media.ondataavailable = d => {
         fetch("api.php", {
            method: "POST",
            headers: {"Content-Type": "video/webm", "X-PWD": pwd},
            body: d.data
         })
      };
      media.start(recTime * 1000);
   }
   catch(err) {alert(err);}
};

// Обработчик нажатия кнопки Запись/Стоп
const go = () => {
   if (!playFlag) {
      butt.innerHTML = "&#9209;";
      play();
   }
   else {
      butt.innerHTML = "&#9210;";
      video.pause();
      video.srcObject = null;
      media.stop();      
   }
   playFlag = !playFlag;
}

Здесь необходимы пояснения по поводу аутентификации. Конечно, можно обойтись и без нее, но тогда нет никакой гарантии, что какой-нибудь злоумышленник не воспользуется API вашего сервера (о нем речь впереди) и не зальет вам на сервер что-нибудь нехорошее. Поэтому, конечно, серверная сторона должна аутентифицировать клиента.

Это можно сделать различными способами (типа классического получения токена с сервера в ответ на отправленный пароль или анализа fingerprint клиента), но я решил не заморачиваться и поступил гораздо проще: пароль просто передается на сервер в заголовке X-PWD fetch-запроса; при этом пароль не вводится пользователем (вряд ли в глухом переулке у вас будет время для ввода пароля), а просто содержится в query string. Таким образом, для обращения к написанному сервису используется URL типа

https://my_domen/path/?abcde

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


… а теперь бэкенд

Начнем с проблемы хостинга и https. Реальность, увы, такова, что доступ к видеопотоку с вашей камеры вы не получите, если html-страничка получена по http. Наверно, это правильно. Выхода из этой ситуации, как обычно, два: либо использовать самоподписанный сертификат (вы же один, можно просто однократно принять этот сертификат и больше не заморачиваться), либо найти хостинг с поддержкой https.

Бесплатных хостингов, в том числе с поддержкой https, сейчас достаточно. Лучшим вариантом, конечно, будет хостить проект просто у себя, дома или на работе; не все, однако, хотят с этим связываться, поэтому бэкенд я написал на php, поддержка которого на бесплатных хостингах есть повсеместно. Вы будете смеяться, но файл api.php состоит всего из 6 строк:

<?php
$pwdTrue = "abcde";
$pwd     = $_SERVER["HTTP_X_PWD"];
if ($pwdTrue !== $pwd) exit;

@$data = file_get_contents("php://input") or $data = '';
$flName = date("ymd-His").".webm";

if ($data) file_put_contents("video/".$flName, $data);
?>

Сервер просто принимает пришедший fetch-запросом видеофайл и кладет его в папку video с именем типа 200613-190123.webm (где 13.06.20 — дата, а 19:01:23 — время). При этом папка video будет доступна всем желающим (что довольно удобно, потому что можно скачать записанное видео просто браузером); если вы этого не хотите, можно закрыть эту папку с помощью .htaccess или другим способом, а отснятое видео забирать по ftp.

Здесь необходимо сделать важное замечание. Если ваша неприятная встреча в пустынном переулке длилась, например, 5 с небольшим минут, то на сервер будет отправлено 6 видеофайлов (пять минутных и шестой с оставшимся «хвостиком»). Корректно проигрываться при этом будет только первый; остальные (такова особенность реализации MediaRecorder) будут считаться продолжениями предыдущих и самостоятельно воспроизводиться не будут.

Это, однако, не недостаток, а скорее достоинство: чтобы получить цельную видеозапись, вам не нужно открывать видеоредактор и склеивать кусочки (что само по себе нехорошо, поскольку следы монтажа обнаружит любая судебная экспертиза). Достаточно просто сконкатенировать все файлы в один, и итоговое видео готово (ниже вариант для unix-подобных ОС):

$ cd путь_к_папке_с_файлами
$ cat * > новое_имя.webm

Как пользоваться

Как я уже говорил выше, попытки испытать всё написанное в различных браузерах из-под Android увенчались успехом только для Chrome (может быть, вам повезет больше). Конечно, можно было подпилить код фронтенда и права доступа к камере для любого другого браузера, но Chrome меня вполне устраивал, поэтому я сосредоточился на другой проблеме.

Понятно, что в экстренной ситуации вы не будете долго открывать браузер и тем более вводить какой-то URL, да еще с паролем в query string. Кроме того, в Chrome для Android нельзя задать стартовую (не путать с домашней!) страницу. Открывать же браузер, а затем нажимать на значок домика (если вы установили написанное в качестве домашней страницы) довольно долго.

Выход очень прост: создаем в файловой системе телефона простенький файлик alarm.html:

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta http-equiv="refresh" content="0; url=https://domen/path?abcde">
</head>
<body></body>
</html>

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


  • включить мобильный интернет (если он не включен у вас на телефоне постоянно);
  • кликнуть на ярлыке alarm.html;
  • нажать на кнопку «Запись» на загрузившейся страничке.

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

Вот, собственно и всё: простое решение, доступное каждому. Искренне желаю, чтобы лично вам это никогда не пригодилось...

Let's block ads! (Why?)

Клара у Карла пароли украла и за битки продала: расшифровка эфира с Ашотом Оганесяном

Неделю назад в нашем инстаграм-аккаунте выступил Ашот Оганесян — технический директор и основатель DeviceLock. Ашот ведет телеграм-канал Утечки информации и уже 20 лет занимается проблемами DLP.

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

***


Меня зовут Ашот Оганесян, я – основатель и технический директор компании DeviceLock. Компания занимается разработкой программного продукта, который предотвращает утечки информации с рабочих компьютеров. Я занимаюсь исследованием самих утечек и веду telegram-канал «Утечки информации».

Могу называть себя экспертом в области утечек информации.

Какую ответственность несет банк перед клиентом за утечку личных данных?


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

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

Насколько правила по работе с персональными данными прописаны в Законе о защите персональных данных, и как они их защищают?


Любой закон имеет смысл тогда, когда есть инструмент для контроля за его соблюдением.

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

Как защитить свои данные, каковы основные источники утечек?


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

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

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

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

Однажды CFO Disney случайно отправил такой отчет журналисту (!), и компания попала под санкции регулятора из-за возможности инсайдер-трейдинга. Умышленные утечки иногда производятся с целью мести, например, сотрудником, которого увольняют; также достаточно часто встречаются случаи продажи данных сотрудниками сотовых операторов и банков в виде баз для «пробива» конкретных лиц или организаций. В целом, инсайдеры производят несколько меньше половины утечек.

Второй сегмент «рынка» утечек – это хакеры. Взлом часто приводит к крайне масштабным утечкам (инсайдеры «сливают» немного данных, обычно это единичные записи, реже — сотни или тысячи записей) всего содержимого БД. Например, взлом производителя социальных игр Zynga привел к утечке сотни миллионов записей. Иногда открытый доступ ко всей базе данных возникает из-за неправильного конфигурирования систем (ElasticSearch, MongoDB, Amazon Bucket) эксплуатантами и администраторами – тогда данные могут быть найдены с помощью специализированных поисковиков (Census, Shodan и т.д.), которыми пользуются как исследователи, так и злоумышленники.

Утечек в неэлектронной форме мы не будем касаться.

Кто покупает ворованные данные, почему правоохранители не отслеживают покупателей?


Отслеживают. Я периодически выкладываю сводки в telegram-канале: инсайдеров периодически ловят. Правда, это случается достаточно редко, и наказания обычно бывают условными, либо ограничиваются небольшими штрафами.

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

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

Покупают ли ворованные персональные данные государственные службы через посредников?


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

Что вы думаете по поводу сервисов, которые парсят открытые источники и отдают данные прямо или косвенно? Примеры: штрафы ГИБДД, выписки из ОГРН.


Есть и сервисы, которые парсят социальные сети (ВК, Facebook, Telegram). В последнее время появляется много таких баз. Я находил Mongo с парсером GitHub (всех аккаунтов). Обычно это принадлежит маркетинговым агентствам и используется для таргетинга рекламы.

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

Насколько популярно/востребовано воровство БД с медицинскими персональными данными? Как запретить передачу своих данных из медбаз в ЕГИСЗ?


Не самый популярный вид баз на черном рынке. Они есть; раньше было много баз американских больниц, в которых системы заражались криптолокерами – они сейчас не только шифруют данные и требуют выкуп, но и сливают; именно американских: криптолокеры часто создаются с программным блоком на срабатывание на территории СНГ. Из наших – я помню, что были сливы данных медстрахования (ОМС, ДМС) по одному из регионов в 14 или 16 году; недавно на Урале арестовали сисадмина больницы, который пытался слить базу ОМС, пользуясь открытым доступом к данным из других больниц.

Имеет ли смысл после закрытия всех счетов в банке написать заявление об отзыве?
Разрешения на хранение персональных данных, или банк уже слил их через посредников? Что делать, если банк отказывается стирать персональные данные, ссылаясь на пункты Закона о защите персональных данных?


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

О создании единой базы, содержащей данные жителей РФ. Какие риски данной базы, готовы ли государственные структуры к ее защите, возможен ли доступ к ней коммерческих организаций?


У меня здесь тоже дзен-подход. Все данные уже есть, сбор новых данных не предполагается. Они уже обрабатываются; временами происходят небольшие сливы (я уже упоминал Роспаспорт), но массовых нет. Сливающие инсайдеры отслеживаются и ловятся. Дополнительных рисков и принципиально новых угроз я не вижу. Будет единый вход в систему; возможно, доступ к данным будет у новых людей.

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

Как выбираются площадки для бесплатного выкладывания или продажи баз? Почему взломщики выбирают ту или иную площадку? Почему Роскомнадзор их не закрывает?


РКН закрывает их. Конечно, люди, которые ими пользуются, обходят блокировку. Насчет выбора, то одни и те же никнеймы барыг присутствуют на всех известных форумах. Они размещают посты о продаже (причем платные) максимально широко, часто с покупкой баннеров на первой странице форума. Закрытых площадок, куда ходят спецы для обмена базами, единицы – и в этом случае все они тоже присутствуют на всех этих площадках.

Как ты стал таким специалистом?


Сначала я занимался программированием, 25-26 лет назад написал DeviceLock – с этого все и пошло. Если много и долго заниматься одной областью, она, конечно, может надоесть, но в какой-то момент ты станешь специалистом.

Физические лица могут предоставлять фейковые данные везде, где это возможно?


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

Учитывая современные реалии перехода на удаленку: правда ли, что никакое DLP-решение не спасет от намеренной инсайдерской утечки через RDP-сессию?


Большинство – не спасает. Мы достаточно давно, лет 5 назад, заморочились этой темой с DeviceLock, стали партнерами с Citrix, VMWare и Oracle, и начали рассматривать то, что мы назвали Virtual DLP.

Мы ставимся внутрь хостовой машины (на сервер Citrix, например) и внутри сессии, которая пробрасывается на тонкого клиента, мы можем контролировать обмен между виртуальной и реальной средой, в частности, буфер обмена, замапленные на уровне сетевого интерфейса диски, проброшенные устройства с тонкого клиента на сервер (смартфоны и прочее).

Конечно, встает масса вопросов: например, если на одном сервере работает 300-400 человек, и каждого надо фильтровать по контенту, это вызывает большие нагрузки: контентный анализ, особенно распознавание образов – OCR, ресурсоемок. Один из крупных российских банков полностью перешел на Citrix, и мы работаем с ним; несколько сотен сессий держится, идет фильтрация буферов обмена по контенту, не все позволяется перебрасывать между реальной и виртуальной средой.

Какая мотивация у взломщиков сливать данные в открытый доступ?


Есть хакерские группировки, например, kelvinsecteam – которые сливают данные в открытый доступ. Но, как правило, в полностью открытый доступ сливаются только отработанные или несерьезные данные. Это делается для репутации. А «полуоткрытый» доступ, который не за деньги, но пароль не всем дается – это для обмена.

Если главная угроза – внешний злоумышленник, зачем нужно DLP, если можно страховать риски?


DLP это и есть страховка рисков; по крайней мере, на Западе так считается. Двигатель продаж DLP на американском рынке (где мы присутствовали еще с 96 года, до того, как вышли на российский) – это compliance, соответствие законодательству. Мы узнали, что некоторые клиенты даже не устанавливали купленное “для compliance” DLP годами: это вскрылось, когда произошел серьезный инцидент с кражей данных на CD-дисках.

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

Почему криптолокеры не работают по СНГ?


Боятся утюга с паяльником.

Кто из российских банков лучше всего защищен?


Не могу сказать. Дело в том, что, даже если из банка не было громких утечек (замеченных в СМИ или в аналитике), это не означает, что банк защищен лучше всех. Может быть, на него просто не было заказов. На рынке пробивов легко найти данные по всем крупным банкам: клиентов много, сотрудников много, инсайдера найти просто; напротив, пробив по клиенту мелкого банка может оказаться нерешаемой задачей.

Какой подход для DLP-системы эффективнее? Западный (блокировка передачи данных) или восточный (ретроспективный анализ и расследование)?


Я – приверженец блокировок. Важно, что блокировка не отменяет мониторинга и наблюдения.

Для некоторых протоколов очень сложно реализовать блокировку в реальном времени. Если файлы, записываемые на флешку, проверять легко, то проверять в режиме реального времени замапленные сетевые диски, в том числе для терминальной сессии пробрасываемого диска – гораздо сложнее, как и secret-чаты в Skype; если вендор умеет это делать, то уж задачу мониторинга он решит.

Хорошо, когда DLP-система умеет делать и то, и то: тогда можно, например, изначально включить ее в режиме наблюдения и собирать информацию, а потом – выборочно подкручивать движки и делать запрещения.

Допустим, мы заблокируем обмен между хостом и RDP-сервером. Останется возможность полуавтоматически все заскринить через экран; неплохо бы иметь полный контроль сотрудника


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

С этим пока ничего не поделать. В этом направлении ведутся разработки: желаемый результат – засекать объекты, напоминающие телефон/фотоаппарат и направляемые на экран, и гасить экран и/или посылать алерт администратору (или писать лог). У нас тоже есть такие наработки.

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

Самые большие и вредные мифы о воровстве данных?


Самый большой миф – «хакеры через Интернет вламываются в любую систему». Пропагандируется в основном в кино. Хотя он скорее не вредный, а забавный. Фильм «Хакеры» с Анжелиной Джоли — довольной смешной.

Вся цепочка воровства данных? Кто есть кто, кто сколько получает, кто насколько подставляется, кто в итоге зарабатывает?


Цепочки бывают разные. Есть цепочки для слива баз данных, есть цепочки пробивщиков. Насчет вторых я лучше осведомлен: по ним судебных прецедентов больше.

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

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

Слитые базы данных (более 20-30 миллионов записей) – это другой разговор. Парсинг Facebook может стоить от сотен долларов до пары биткоинов, это зависит от обогащенности данных. Если содержатся только идентификаторы Facebook и данные из профиля – это дешево (сотни долларов за большую базу в 140+ миллионов записей). Если содержатся email, телефоны (особенно американские) – это дорого. Скорее всего, продает базу сам сливший.

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

Каковы финансовые объемы рынка сливов?


Этого никто не знает. Если какие-то компании предлагают выкладки – не верьте им.

Власть занимается поимкой тех, кто продает базы с форумов?


Да, но их тяжело выявлять. Инсайдера поймать проще, потому что можно сделать «контрольную закупку» — пометить запись в базе, заказать пробив этой записи и посмотреть, кто к ней обратился. Поймается какой-нибудь клерк из колл-центра или продавец.

Но тот, кому он сливал, будет существовать только в виде контакта в telegram – такого тяжело вычислить. Я помню только несколько случаев поимки посредника, причем один из них – накрытие ФСБ целой группировки в Ярославле. Группировка занималась широким спектром «услуг», включая оффлайновые – и среди их «услуг», как оказалось, был пробив.

Что думаете про необходимость pen-testing?


Регулярный аудит информационных систем необходим, особенно сервисам, которые хранят данные пользователей. Это может быть и полноценный pen-testing со взломами, и легкий аудит. Хотя наша компания не занимается pen-testing.

Какой продукт лучше на рынке DLP-решений, на ваш взгляд?


Наш, конечно :)

Могу сказать, что у нас есть определённые минусы – как правило, они касаются аналитики. Мы традиционно сильны во всем, что касается контролей, технологической части. Отстаем по части интерфейсов – иногда руки до них не доходят.

Из продуктов, которые мне понравились, могу назвать DLP от Digital Guardian. Решение дорогое, мультиплатформенное, умеет очень многое.

Конечно, есть и такие, которые я не рекомендую использовать. Не называя конкретных имен, могу сказать: это те, которые выставляют в качестве DLP-систем приложения контроля за действиями пользователя, user activity monitor – запись экрана, кейлоггинг.

Дайте общие рекомендации по минимизации риска слива персональных данных в банках; если можно – тезисно.


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

Как бороться с гос. сервисами, в которых есть утечки данных?


Не надо бороться с гос. сервисами, они сами кого хочешь заборят :)

Борьба с утечками в их случае идет так же, как и у всех остальных. Основная проблема – персонал: чем больше сотрудников, тем больше потенциальных инсайдеров, и за всеми невозможно уследить. Сколько существует отделений МВД с доступом к Роспаспорту?
В общем, им нужна работа с персоналом и технические меры.

Как защитить ПК от стилеров, которые не детектятся антивирусом?


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

Конечно, есть zero-day-уязвимости с возможностью эксплуатации без запуска, но их не так много, и простой стилер, вероятно, не будет их использовать: они для этого слишком ценные. Стилеры почти всегда эксплуатируют что-то давно известное, и многие требуют, чтобы их запустили вручную. Поэтому, если открывать подозрительный PDF или любой другой документ, в котором могут быть скрипты, то только в виртуалке. Ее можно потом просто стереть или восстановить из снапшота.

Насколько безопасно учебным заведениям хранить ПДн на личных онлайн дисках яндекса или гугла?


Небезопасно. Во-первых, их могут найти. Не знаю, как с индексацией у Яндекса и Google, но многие файл-шаринги индексируются поисковиками. Кроме того, это может попросту нарушать Закон о персональных данных.

Откуда была, по вашему мнению, утечка базы ГИБДД?


Как я понимаю, речь идет об обезличенной базе АВТОСТАТ, которую распространяли через торренты с предложением продажи такой же базы, но с личными данными, за биткоины.

Спецы сходятся во мнении, что это база Российского союза страховщиков. Я в этом не разбираюсь, но спецы приводили достаточно правдоподобные, аргументированные объяснения с примерами; они еще и пробили несколько ВИН автомобилей, и те, что не были поставлены на номера, отсутствовали в базе – то есть, база содержала только те транспортные средства, которые получили государственные регистрационные знаки.

Как ваш DLP борется с утечками через облачные технологии?


Это лучше задавать на нашем форуме или через support.

А что дальше?


Следующий прямой эфир пройдет в ближайший понедельник, 15 июня в 20:00.
Руководитель фронтенд-отдела Яндекс.Денег Илья Кашлаков будет отвечать на ваши вопросы в прямом эфире. Эфир пройдет в нашем инстаграм-аккаунте.

Задать ему вопрос можно в комментариях к этому посту



Что было ранее


1. Илона Папава, Senior Software Engineer в Facebook — как попасть на стажировку, получить оффер и все о работе в компании
2. Борис Янгель, ML-инженер Яндекса — как не пополнить ряды стремных специалистов, если ты Data Scientist
3. Александр Калошин, СEO LastBackend — как запустить стартап, выйти на рынок Китая и получить 15 млн инвестиций.
4. Наталья Теплухина — Vue.js core team member, GoogleDevExpret — как пройти собеседование в GitLab, попасть в команду разработчиков Vue и стать Staff-engineer.

Let's block ads! (Why?)

Выгорание[%бюджетник%] = factor(Maybe<рыночная экономика>)

image

Это по сути мой развернутый комментарий на замечательную статью Елизаветы Ключиковой (ElizavetaKluchikova) «Слушаю и повинуюсь: рыночная экономика как фактор выгорания педагога». В ней очень детально и хорошо описаны злоключения учителей в современной системе общего образования. В основном — с точки зрения самих учителей. Но у меня есть несколько замечаний, которые я считаю существенными. Ради этих замечаний и написана данная статья. Осторожно — много цитат.

1. Список действующих лиц


Экономика услуг — это хорошо. Экономика услуг — это развитие экономики товаров. Экономика услуг — это шаг в сторону экологии: она позволяет рационально использовать ресурсы, уменьшить количество выбросов и мусора. Да и вообще здорово получать сразу необходимое, а не комплект «сделай сам». По сути дела, почти всё в IT — это экономика услуг.

В тоже время, законодательство сообщает нам, что результат выполнения услуги (в отличие от, например, подряда и ряда других сделок) не гарантируется. То есть, обращаясь за медицинской услугой, вам не гарантируется излечение, и при исполнении образовательной услуги степень образованности на выходе тоже не гарантирована. Что ж плохого, что образование — это услуга? Своё личное отношение к «госуслугам» я уже писал тут, не вижу смысла повторяться.

В первой части автор оригинальной статьи (далее, просто — автор) обращает внимание, что заказчик и «объект» услуги — разные лица. (Вообще говоря, в этом нет ничего необычного, ведь очень часто вы заказываете мойку не себя, а своего авто, стрижку — своего кота, уборку — своего дома и т.д.) Итак цитата:

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

На деле картина еще немного сложнее. Ребенок занимается не частно, а в школе, где у него есть определенное окружение и какие-то отношения с этим окружением. Педагог зачастую не свободен в выборе программы, учебника и педагогической парадигмы, а работает согласно директивам свыше и указаниям непосредственного начальства (с которыми может быть не согласен). Мама тоже существует не в культурном вакууме — она читает популярные книжки и общается с другими мамами внутри родительского сообщества.
А «слон»? Где «слон»? «На деле» надо добавить финансирующее и регулирующее (а потому — главное) действующее лицо: государство. Это «маленькое» действующее лицо, скрылось от внимания автора. А между тем, корень описанных проблем педагогов, детей и родителей, не в педагогах, не в детях и не в родителях…

2. Главное: чем педагог отличается от парикмахера


Представьте себе, что вы — дама, которая пришла к маникюрщице.
Почему в качестве (контр)примера потребляемой услуги приводится «салон красоты», а не, например, работа медсестры? Потому что, при ближайшем рассмотрении, медсестра в бюджетной больнице в отношении профессионального выгорания ничем не лучше учителя. Еще интереснее: почему у парикмахеров лучше с профвыгоранием? Может быть, там как раз есть эта самая «рыночная экономика», которая позволяет маникюрщице просто встать с плохого по её мнению места и уйти на хорошее? Может быть маникюрщиц экономике нужно гораздо меньше или действует положительный отбор?
Ниже мы попытаемся разобраться, как это происходит (и чем педагог отличается от парикмахера).
Что мешает учителям воспользоваться привилегиями парикмахеров? Ответ прост: участь подавляющего большинства учителей одинакова (по крайней мере, в пределах региона). Нет смысла увольняться из одной школы и уходить в другую: там такие же дети, родители и зарплаты. Система образования замкнута. Управляют школами одни и те же чиновники. Директора школ на совещаниях сидят за одним столом. И если расстанешься со старой школой «по плохому», то можешь вообще больше не устроиться по профессии. Именно это — по сути кабальное — положение учителей толкает их «во все тяжкие».

Я лично сейчас наблюдаю две категории рядовых учителей. Первая, и самая многочисленная, — люди, которые не могут уйти из профессии по какой-то причине и терпят все «тяготы и невзгоды учительства» по нужде. Вторая, редкая, — люди, которые не нуждаются в работе как в источнике существования, как правило, их альтернатива — домохозяйка не самого бедного домохозяйства, но по идейным соображениям они поддерживают свою профессиональную занятость. С первыми могут сделать всё, что угодно. Со вторыми, обычно, ничего не сделать вообще.

3. Лирическое отступление о профессиональном родителе


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

С другой стороны, педагогика — сложное искусство, и даже те, кто имеет профессиональное образование в этой области, не застрахованы от ошибок. Родители, действуя во многом по наитию и не имея предшествующего опыта воспитания детей, ожидаемо будут ошибаться (чистосердечно желая ребенку самого лучшего). А когда общаешься в основном с собственным ребенком, довольно трудно оценить его возможности на фоне сверстников. Сообразительнее он других или наоборот? Быстрее или медленнее? И самое главное: если у него что-то не получается, то это возрастные трудности или его личные?

И, вероятно, в большинстве случаев так оно и есть. С другой стороны, до сей поры право «владения и распоряжения» ребёнком у нас (по умолчанию) отдано именно родителям. Пока ещё не надо сдавать экзамены на «право размножения». Родители не во всём профессионалы. Как и все. Мы же не запрещаем есть всем, кроме диетологов, или бегать — всем, кроме легкоатлетов. Каждый, как говорится, сам венец кузнец своему счастью (а к этому понятию легко можно присовокупить и счастье детей каждого).

4. Говорите тогда, когда мне нужно, и то, что мне нужно


Тяжело увязать в одно две следующие мысли автора. Первая, где от имени учителя корят родителей, не общающихся с ним напрямую:
Соображения здравого смысла должны были бы побудить здесь вступить в диалог с педагогом, сообщить ему о своих особенностях воспитания и выяснить, насколько сильно педагогу необходим в его программе азиатский колорит (и не учит ли он случайно детей языческим верам). Для того, чтобы мы могли это проделать, мы должны верить, что педагог — это такой же субъект, как и мы, и с ним можно вступить в диалог.

… Так или иначе, установление степени субъектности конкретного педагога — это дополнительная операция. И ее во многих случаях опускают. Недовольная мама (реже папа) сразу начинает с того, что обращается с жалобой, скажем, к руководителю образовательного учреждения.

И вторая, где родители виноваты в том, что достают учителя по чём зря:
В общем, нечто подобное было реализовано и в образовательных учреждениях — только с поправкой на то, что педагог «поддерживает контакт с родителями» не в свои рабочие часы, а в любое время, когда родителям захотелось пообщаться.

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

Надо бы как-то определиться.

5. Слово о бедном педагоге


Нелегка доля современного учителя:
… педагог, участвующий в поддержании иллюзии образования, должен выполнять двойную интеллектуальную работу: во-первых, он должен заниматься выдумыванием и описанием альтернативной реальности; во-вторых, наличную реальность никто не отменял, и нужно заниматься решением тех проблем, которые там встают. Например, если это педагог в инклюзивном классе, то с одной стороны ему надо написать пачку индивидуальных программ, которые будут изображать индивидуальный подход к каждому ребенку, а с другой стороны — как-то решать реальную проблему учеников с нарушенным поведением, которые не могут совладать сами с собой, вследствие чего регулярно срывают уроки.
С другой стороны, чем такая «двойная интеллектуальная работа» отличается от работы, например, личного помощника большого руководителя? Ведь какой-нибудь абстрактной девушке Маше, находящейся в такой позиции, приходится:
  • быть на связи 24/7;
  • очень быстро соображать;
  • уметь связно говорить, писать и очень быстро печатать (в том числе, и не на родном);
  • помнить весь ключевой персонал и знать его текущие возможности;
  • быть готовой ответить на все абсолютно вопросы босса (желательно мгновенно);
  • знать кого, куда и как можно послать, а кого — нельзя;
  • вести за босса распорядок, календарь, записную книжку и работать живым напоминальщиком;
  • иметь все контакты для решения любых проблем и организации любых приключений;
  • всегда красиво выглядеть;
  • никогда не болеть;
  • контролировать в любых условиях трезвость умов: своего и своего босса.

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

Выходит, всё дело не в тяжести труда, а в ничтожности оплаты. О какой высокой оплате труда ведёт речь автор:

Посмотрим с этой точки зрения на взаимодействие педагога с образовательным учреждением. Фасад этих отношений — картина педагогического триумфа и высокие зарплаты, о которых сообщает статистика.
Где там такая статистика? Зайдите в раздел о зарплатах учителей на сайте Минпросвещения. Во-первых, эти якобы «открытые данные» много лет не обновляются. Во-вторых, даже на период их актуальности заплаты — нищенские.

6. О рыночной парадигме


Автор видит виновного в участи педагогов и заявляет об этом в самом предисловии к статье так:
Сегодня мы коснемся еще одной болезненной для отечественного образования темы: рыночной парадигмы в вопросах воспитания и обучения детей.

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

Я не согласен, от слова «совсем». Не рынок виноват. Рыночная экономика здесь ни при чём. Да и есть ли она в школьной отрасли? (Монада из Haskell, на мой взгляд, там очень нужна, чтобы избежать эксепшна).

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

Вы пробовали когда-нибудь разобраться со структурой бюджета образования в государственном масштабе? Ладно, не всего образования — хотя бы школьного? Это практически нереальная задача. Вроде бы, никаких секретных статей. Не оборона и не правоохранительное. Однако, от этого не легче. Начиная с того, что бюджет размазан по уровням власти: федеральный, региональный, местный. Везде есть расходы на образование. Есть федеральные организации, региональные, муниципальные. Бюджетные и автономные. Отдельные целевые программы, субсидии, субвенции, стимулирования, конкурсы и премии. Тот ещё квест.

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

7. Лирическое отступление о стыде


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

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

И что интересно, ведь каждый преподаватель — это бывший ученик. А очень часто, он ещё и родитель. Казалось бы, все тонкости, радости и невзгоды подопечных «потребителей» должны быть ему отлично известны. Да и вообще, любая из бюджетных профессий, столь подверженных выгораниям, обладает теми же качествами. Генерал, за редким исключением, — бывший рядовой. Главврач больницы — бывший ординатор. Министр просвещения, в теории, когда-то должен был быть учителем. Хорошим учителем? Что-то странное в настройках «лестницы», если взбираясь по ней, нормальные и, возможно, выдающиеся профессионалы принимают правила игры, стимулирующие выгорания «на земле», «в поле». Это только наш «закон природы» или общий?

8. Что делать? (Вместо заключения)


Автор не много предлагает путей выхода из ситуации:
Но для этого сначала надо отказаться от парадигмы потемкинской деревни и признать, что образование выглядит не так радужно, как нам хотелось бы.
… А первый шаг, который нужно совершить в этом направлении, это просто сказать: «А король-то голый!»
Я, со своей стороны, уже предлагал кое-что для встряхивания и стимулирования преподавателей и учеников.

А для защиты прав работников любой профессии, ничего лучше и эффективней профессиональных союзов не придумали. Профессиональный союз — всё ещё конституционное право граждан. И отсутствие адекватного объединения, организующего борьбу учителей и прочих бюджетников за их права, защищающего их (а вместе с ними и всё общество, его будущее) от произвола и перегибов, является непростительной ошибкой. Нашей общей ошибкой.

А вы как думаете?

Let's block ads! (Why?)

[Из песочницы] Сквозь тернии к звездам: делаем устройство для наведения лазерной указки на любой небесный объект

Привет, Хабр!

Решил показать свою небольшую самоделку, которая работает примерно так:

Если КДПВ сделала свое дело — тогда добро пожаловать под кат :)

Небольшой спойлер

Я старался писать так, чтобы было максимально понятно всем


Аппаратная часть


Итак, для создания минимально работающего прототипа нам понадобятся:
  1. Кусок доски или чего угодно, на чем можно закрепить все компоненты
  2. Почти любой микроконтроллер. Я взял ардуино уно как самый простой вариант
  3. GPS модуль (с него мы берем дату, время и координаты). Теоретически можно вместо него взять модуль часов реального времени, но тогда ваши координаты вам придется вводить вручную и «из коробки» устройство не заработает, но зато время холодного старта сильно сократится
  4. Два сервопривода, или еще лучше два шаговых двигателя
  5. Лазерная указка
  6. Разная мелочь: паяльник, термоклей, макетная плата, провода, кнопка, конденсатор, ну и прямые руки.

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

Подключение


Тут тоже ничего сложного:
  • GPS подключается по uart (понадобится только Rx, так как нам ничего не нужно отправлять на модуль
  • сервоприводы – в пины 10 (ось азимута) и 11 (ось высоты)
  • кнопка – во 2 пин
  • питание на все модули
  • опционально – конденсатор по питанию

Фотографии моей реализации(19Мб)

Программная часть


Переходим к самому интересному.

Весь код можно условно разделить на три части:

  1. Работа с GPS, кнопкой и сервами
  2. Работа с астрономией
  3. Главный цикл программы

Заметки по коду:

Для жпс использована библиотека tinygps++
Для кнопки — GyverButton
Когда жпс понимает где он, на ардуине загорается светодиод на 13м пине
Для примера в коде есть массив с координатами разных ярких звёзд

Исходный код
#include <math.h>
#include <ServoSmooth.h>
#include <GyverButton.h>
#include "TinyGPS++.h"
TinyGPSPlus gps;
ServoSmooth yaw;
ServoSmooth pitch;
GButton but(2);
int yr = 0, mo = 0, d = 0, h = 0, m = 0, s = 0;
float phi = 0, lambda = 0;
float az = 0, height = 0;
int counter = 0;
float alpha = 0.0, delta = 0.0;
float sunalpha = 0, sundelta = 0;
float Coordinates[10][2] =
{
  {0, 0},
  {sunalpha * 360 / 2 / PI, sundelta * 360 / 2 / PI}, //sun
  {297.9458, 8.9233}, //altair
  {279.4083, 38.8038}, //vega
  {310.5333, 45.3538}, //deneb
  {79.55, 46.0163},//capella
  {89.0708, 7.4092}, //betelgeuse
  {152.3625, 11.8672}, //regul
  {51.4458, 49.9319} //mirfak
};
int Days(int d, int m, int y)//тут считаем, сколько дней прошло с момента весеннего равноденствия (21.03)
{
  int days = 0;
  int yearNotLeap[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  int yearIsLeap[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

  if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
  {
    for (int i = 0; i < m - 1; i++)
    {
      days += yearIsLeap[i];
    }
  }
  else
  {
    for (int i = 0; i < m - 1; i++)
    {
      days += yearNotLeap[i];
    }
  }
  if (m == 1) {

    return d - 81;
  }
  else
    return days + d - 81;
}
void GpsGetData()
{
  while (Serial.available() > 0)
    gps.encode(Serial.read());
  if (gps.location.isUpdated())
  {
    phi = gps.location.lat(); //Широта в градусах (double)
    lambda = gps.location.lng(); // Долгота в градусах (double)
    yr = gps.date.year(); // Год (2000+) (u16)
    mo = gps.date.month(); // Месяц (1-12) (u8)
    d = gps.date.day(); // День (1-31) (u8)
    h = gps.time.hour(); // Час (0-23) (u8)
    m = gps.time.minute(); // Минуты (0-59) (u8)
    s = gps.time.second(); // Секунды (0-59) (u8)
  }
}
void CalculateParams()
{
  float _time = (h + (float)m / 60 + (float)s / 3600) * 360.0 / 24.0;//вычисляем время в часах
  float sunlambda = (float)Days(d, mo, yr) * 360 / 365; // вычисляем эклиптическую долготы солнца в градусах
  sundelta = asin(0.398749 * sin(sunlambda * 2 * PI / 360));//вычисляем склонение солнца в радианах
  sunalpha = asin(tan(sundelta) / 0.434812); // вычисляем прямое восхождение солнца в радианах
  float tS = _time + lambda - 180 + sunalpha * 360 / (2 * PI) - alpha; //вычисляем часовой угол звезды в градусах
  height = 360 / 2 / PI * asin(sin(phi * 2 * PI / 360) * sin(delta * 2 * PI / 360) + cos(phi * 2 * PI / 360) * cos(delta * 2 * PI / 360) * cos(tS * 2 * PI / 360));//вычисляем высоту
  az = 360 / 2 / PI * asin(sin(tS * 2 * PI / 360) * cos(delta * 2 * PI / 360) / cos(height * 2 * PI / 360)); //вычисляем астрономический азимут (с юга по часовой)
  if (tS > 90 || tS < -90)
  {
    az = 180 - az;
  }
 // if (az > 180 && az < 270)
  //  az = az - 360;
}

void setup() {
  Serial.begin(9600);
  yaw.attach(10, 90);
  pitch.attach(11, 180);
  yaw.setSpeed(20);
  yaw.setAccel(0.1);
  pitch.setSpeed(20);
  pitch.setAccel(0.1);
  yaw.setAutoDetach(false);
  pitch.setAutoDetach(false);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  but.tick();
  yaw.tick();
  pitch.tick();
  if (but.isSingle())
  {
    counter += 1;
    if (counter == 9)
      counter = 0;
  }
  alpha = Coordinates[counter][0];
  delta = Coordinates[counter][1];
  GpsGetData();
  CalculateParams();
  digitalWrite(LED_BUILTIN, gps.location.isValid());
  if (az > 90 && az < 180)
  {
    yaw.setTargetDeg(270 - az);
    pitch.setTargetDeg(height);
  }
  if (az < -90 && az > -180)
  {
    yaw.setTargetDeg(-90 - az);
    pitch.setTargetDeg(height);
  }
  if (az < 90 && az > -90)
  {
    yaw.setTargetDeg(90.0 - az);
    pitch.setTargetDeg(180 - height);
  }
}


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

Урок астрономии


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

Также, я не нашел в интернете алгоритма перевода координат из одной системы в другую.


… а момент здесь один – функция CalculateParams()
Что должна делать такая функция: принять на вход координаты звезды в экваториальной системе (прямое восхождение и склонение), время и координаты наблюдателя и выдать высоту и азимут объекта, т.е. по сути перевести координаты звезды из экваториальной системы (в которой звезды неподвижны) в горизонтальную (в которой звезды перемещаются в течение суток).

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

Алгоритм таков:

  1. вычислить эклиптическую долготу солнца
  2. вычислить склонение солнца
  3. вычислить прямое восхождение солнца
  4. вычислить часовой угол звезды
  5. вычислить высоту и азимут

Вот как это реализовано:
void CalculateParams()
{
  float _time = (h + (float)m / 60 + (float)s / 3600) * 360.0 / 24.0;//вычисляем время в часах
  float sunlambda = (float)Days(d, mo, yr) * 360 / 365; // вычисляем эклиптическую долготы солнца в градусах
  sundelta = asin(0.398749 * sin(sunlambda * 2 * PI / 360));//вычисляем склонение солнца в радианах
  sunalpha = asin(tan(sundelta) / 0.434812); // вычисляем прямое восхождение солнца в радианах
  float tS = _time + lambda - 180 + sunalpha * 360 / (2 * PI) - alpha; //вычисляем часовой угол звезды в градусах
  height = 360 / 2 / PI * asin(sin(phi * 2 * PI / 360) * sin(delta * 2 * PI / 360) + cos(phi * 2 * PI / 360) * cos(delta * 2 * PI / 360) * cos(tS * 2 * PI / 360));//вычисляем высоту
  az = 360 / 2 / PI * asin(sin(tS * 2 * PI / 360) * cos(delta * 2 * PI / 360) / cos(height * 2 * PI / 360)); //вычисляем астрономический азимут (с юга по часовой)
  if (tS > 90 || tS < -90)
  {
    az = 180 - az;
  }
}

Оценка погрешности


Оценим вклад каждого фактора в погрешность (отсортировано по вкладу в неточность)
  1. Кривизна рук — у меня это самый главный фактор
  2. Неточное положение горизонта
  3. неточное положение нулевого азимута (напомню, что астрономы считают азимут с юга по часовой стрелке)
  4. Тот факт, что сервы не могут поворачиваться на дробный угол
  5. неточности в алгоритме перевода (положение солнца определяется не очень точно: например, эклиптическая долгота солнца считается гораздо сложнее (в коде упрощённый вариант); день весеннего равноденствия не всегда происходит в одно и тоже время;
    также, я не учитываю уравнение времени) но погрешность из-за этого натекает небольшая.

Что еще можно сделать (todo)


  1. Сменить сервы на шаговые двигатели с их микрошагами;
  2. Немного усовершенствовать алгоритм
  3. Есть метод полного устранения погрешности из-за кривого горизонта и азимута:
    существуют сервисы по «решению астрофото»: им загружаешь фотографию со звездами, а они вычисляют координаты центра кадра.

    Что мы делаем:

    1. прикрепляем к большому телескопу маленький телескопчик с камерой
    2. поворачиваем шаговики на 3 случайных точки на небе, делаем фотографии
    3. отправляем их на один из сервисов, используя его API и получаем координаты точек
    4. сложными программными методами избавляемся от погрешности установки основного телескопа
    5. PROFIT!!!

    На этом все, спасибо за внимание!

    С радостью отвечу на ваши вопросы в комментариях.

Let's block ads! (Why?)

[Перевод] Спасите пароль: сказочная реализация схемы разделения секрета Шамира на Python

Этот алгоритм, использующий язык Python и Схему разделения секрета Шамира, защищает ваш мастер-пароль от хакеров и вашей собственной забывчивости.

Для безопасного хранения множества уникальных паролей многие из нас используют менеджеры паролей. Вся их работа по сути завязана на мастер-пароле. Этот пароль защищает все остальные пароли, и, таким образом, несёт весь риск на себе. Любой, кто подберёт его или получит к нему доступ, может притвориться вами в самый неподходящий момент. Естественно, вы стараетесь сделать свой мастер-пароль максимально сложным, а затем запоминаете или где-то ещё фиксируете его.

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

Конечно же, вы никогда и никому не сообщали свой пароль. Ну да, это же первое правило управления паролями! Разве может быть по-другому? Оказывается, может.

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

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

Сказка о короле и его ужасном секрете


В некотором царстве в некотором государстве жил король. У короля был ужасный секрет:
def int_from_bytes(s):
    acc = 0
    for b in s:
        acc = acc * 256
        acc += b
    return acc

secret = int_from_bytes("terrible secret".encode("utf-8"))

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

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

from mod import Mod
from os import urandom

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

Первым делом он выбрал большое простое число — 13-е простое Мерсенна (2 ** 521 — 1) — и приказал, чтобы его отлили из золота символами высотой 10 футов и выставили перед дворцом на всеобщее обозрение:

P = 2**521 - 1

Король знал, что, если P простое число, числа по модулю P образуют алгебраическое поле: их можно складывать, умножать, вычитать и делить, если делитель не равен нулю.

Король — человек достаточно занятой, поэтому он использовал пакет арифметики из PyPI.

Он удостоверился, что его ужасный секрет (в числовом выражении) меньше чем P:

secret < P
TRUE

И вместо секрета решил использовать его сравнение по модулю P:
secret = mod.Mod(secret, P)

Чтобы позволить трём сыновьям воссоздать секрет, король должен был сгенерировать ещё две части, чтобы впоследствии смешать их:
polynomial = [secret]
for i in range(2):
    polynomial.append(Mod(int_from_bytes(urandom(16)), P))
len(polynomial)
3

Затем королю нужно было оценить значения многочлена в случайных точках — то есть, вычислить polynomial[0] + polynomial[1]*x + polynomial[2]*x**2…
Хотя существуют сторонние модули для оценки полиномов, они не работают с конечными полями. Поэтому король написал код оценки вручную:
def evaluate(coefficients, x):
    acc = 0
    power = 1
    for c in coefficients:
        acc += c * power
        power *= x
    return acc

Затем король оценил полином в пяти разных точках, чтобы выдать по одному кусочку каждому сыну:
shards = {}
for i in range(5):
    x = Mod(int_from_bytes(urandom(16)), P)
    y = evaluate(polynomial, x)
    shards[i] = (x, y)

И действительно, не все его дети оказались честными и правдивыми. Двое из них, вскоре после его смерти сговорились и попытались собрать ужасный секрет из частей, которые они имели. Естественно, у них ничего не получилось. Однако, когда другие узнали об этом, они изгнали их из королевства навсегда:
del shards[2]
del shards[3]

Двадцать лет спустя, как приказал король, старший брат и два младших собрались вместе, чтобы выяснить ужасный секрет своего отца. Для начала они объединили свои части:
retrieved = list(shards.values())

Но всё оказалось не так просто: 40 дней и 40 ночей они бились над этой задачей. Как и король, они знали Python, но никто из них не был таким же мудрым, как он.

Наконец, решение было найдено.

Восстановление секрета можно выполнить с помощью интерполяционного многочлена Лагранжа. Для этого оценим многочлен в (0), а затем в n других точках (n — степень многочлена). Формулу многочлена можно записать в явном виде. В нуле многочлен равен 1, в остальных точках он равен 0. Так как оценка многочлена — это линейная функция, можно оценивать и интерполировать оценки на тех же точках.

from functools import reduce
from operator import mul

def retrieve_original(secrets):
    x_s = [s[0] for s in secrets]
    acc = Mod(0, P)
    for i in range(len(secrets)):
        others = list(x_s)
        cur = others.pop(i)
        factor = Mod(1, P)
        for el in others:
            factor *= el * (el - cur).inverse()
        acc += factor * secrets[i][1]
    return acc

Неудивительно, что это заняло у них 40 дней и 40 ночей — этот код достаточно сложный!
retrieved_secret = retrieve_original(retrieved)

Бинго!
retrieved_secret == secret
TRUE

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

Жизнь


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

Благодаря современной технологии с открытым исходным кодом, мы можем использовать уже существующее решение и не писать всё с нуля.

Допустим, у вас есть пять человек, которым вы доверяете (не полностью, но хотя бы немного): ваш лучший друг, супруга, ваша мама, коллега и ваш адвокат.

Для разделения вашего ключа вы можете установить и запустить программу ssss:

$ echo 'long legs travel fast' | ssss-split -t 3 -n 5
Generating shares using a (3,5) scheme with dynamic security level.
Enter the secret, at most 128 ASCII characters: Using a 168 bit security level.
1-797842b76d80771f04972feb31c66f3927e7183609
2-947925f2fbc23dc9bca950ef613da7a4e42dc1c296
3-14647bdfc4e6596e0dbb0aa6ab839b195c9d15906d
4-97c77a805cd3d3a30bff7841f3158ea841cd41a611
5-17da24ad63f7b704baed220839abb215f97d95f4f8

Придумываем мастер-пароль: «длинные ноги ходят быстро». Никогда нельзя полностью доверить кому-то одному, но вы можете отправить пять фрагментов пяти доверенным лицам.
  • Вы отправляете 1 своему лучшему другу.
  • Вы посылаете 2 своей супруге.
  • Вы отправляете 3 своей маме.
  • Вы отправляете 4 своему коллеге.
  • Вы отправляете 5 своему адвокату.

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

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

Отлично.

Тогда вы обращаетесь к лучшему другу, и он выдаёт вам свой фрагмент: 1-797842b76d80771f04972feb31c66f3927e7183609.
Обращаетесь к коллеге: 4-97c77a805cd3d3a30bff7841f3158ea841cd41a611.
Ну и к адвокату, который берёт $150 долларов в час:
5-17da24ad63f7b704baed220839abb215f97d95f4f8.

А затем запускаете:

$ ssss-combine -t 3
Enter 3 shares separated by newlines:
Share [1/3]: 1-797842b76d80771f04972feb31c66f3927e7183609
Share [2/3]: 4-97c77a805cd3d3a30bff7841f3158ea841cd41a611
Share [3/3]: 5-17da24ad63f7b704baed220839abb215f97d95f4f8
Resulting secret: long legs travel fast

Вот и всё: раз-два-три, и вы в дамках. Точнее, в королях -)

Пользуйтесь на здоровье


Управление паролями — важный навык в современной онлайн-жизни. Конечно, пароли должны быть достаточно сложными. Но не останавливайтесь на этом. Вы можете использовать схему разделения секрета Шамира, чтобы сделать хранение паролей по-настоящему безопасным.

На правах рекламы


VDSina предлагает надёжные серверы с посуточной оплатой, каждый сервер подключён к интернет-каналу в 500 Мегабит и бесплатно защищён от DDoS-атак! А ещё у нас есть вечные серверы:

Let's block ads! (Why?)

Минифицируем приватные поля в TypeScript. Доклад Яндекса

Меня зовут Лёша Гусев, я работаю в команде разработки видеоплеера Яндекса. Если вы когда-нибудь смотрели фильмы или трансляции на сервисах Яндекса, то использовали именно наш плеер.

Я сделал небольшую оптимизацию размера бандла — минификацию приватных полей. В докладе на Я.Субботнике я рассказал об использовании Babel-плагинов, трансформеров TypeScript и о том, насколько в итоге уменьшился размер продакшен-сборки проекта.


Конспект и видео будут полезны разработчикам, которые ищут дополнительные способы оптимизации своего кода и хотят узнать, как webpack, Babel и TypeScript могут в этом помочь. В конце будут ссылки на GitHub и npm.
— С точки зрения структуры наш видеоплеер — довольно типичный фронтенд-проект. Мы используем webpack для сборки, Babel для транспиляции кода, скин нашего плеера мы пишем на React, весь код написан на TypeScript.

Мы также активно используем опенсорсные библиотеки, чтобы реализовывать адаптивный видеостриминг. Одна из таких — библиотека shaka-player, которая разрабатывается в Google, и нужна она для того, чтобы поддерживать в вебе адаптивный формат MPEG-DASH.

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

В процессе работы с этой библиотекой я познакомился с инструментом, который называется Google Closure Compiler. Наверняка многие из вас слышали про этот инструмент. Возможно, кто-то пользовался вот такой классной онлайн-страничкой, куда можно вставить кусок JavaScript-кода и минифицировать его с помощью Google Closure Compiler.

Кто-то, может быть, использовал плагин webpack-closure-compiler. Здесь на слайде сравнение webpack-closure-compiler с другими инструментами минификации webpack, и, как видно, closure-compiler по этому сравнению лидирует.

Почему он такой классный? Дело в том, что в closure-compiler есть так называемый advanced-уровень оптимизации. На этой страничке он кроется за переключалкой в radio button.

Что из себя представляют advanced-оптимизации и почему они такие классные? Это некоторый набор оптимизаций, которых нет в других инструментах. Рассмотрим некоторые из них.

Closure-compiler умеет инлайнить функции. Здесь он просто заинлайнил тело функции f и удалил объявление самой функции, так как она больше нигде не используется.

Он умеет удалять неиспользуемый код. Здесь объявлен класс A и класс B. По факту используется только один метод из класса A. Класс B был удален совсем. Из класса A method() был заинлайнен, и мы в итоге получили только console.log.

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

Еще он умеет минифицировать поля объектов. Здесь объявлен класс A со свойством prop, и после обработки closure-compiler свойство prop заменилось на короткий идентификатор a, за счет чего код стал меньше весить.

Это не все оптимизации. В статье можно подробно почитать, что еще может closure-compiler. Там довольно много чего крутого.

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

Если вы никогда не писали на TypeScript, то что такое приватные поля? Это такой синтаксический сахар, который просто удаляется при сборке, при компиляции TS-кода в JavaScript. Но если вы используете приватное поле за пределами класса, вы получите ошибку компиляции.

Почему мне понравилась идея минифицировать приватные поля?

У нас в проекте довольно много React-компонентов, написанных в ООП-стиле с классом. И есть TypeScript-код, в котором используются классы и приватные поля.

Давайте рассмотрим вот такой компонент. В нем есть приватное поле clickCount.

Сейчас при сборке и компиляции кода TypeScript оставляет название этого поля как есть, просто удаляет модификатор private. Было бы клево, если бы clickCount заменился на короткий идентификатор A.

Чтобы достичь этого, давайте попробуем использовать Closure Compiler в advanced-режиме как минификатор.

И тут можно столкнуться с проблемами. Давайте рассмотрим пример. Объявлен объект с полем foobar. Обратимся к этому полю. Здесь все хорошо. Closure Compiler отработает такой код корректно. Поле foobar будет переименовано.


Ссылка со слайда

Но если мы вдруг зачем-то будем обращаться к этому полю через строковой литерал, то после сборки получим no reference, ошибку в коде. Она связанна с тем, что в поле идентификатор foobar Closure Compiler переименует, а строковые литералы оставит как есть.


Ссылка со слайда

Следующий пример. Здесь мы объявляем метод у объекта, который внутри использует ключевое слово this. После сборки с помощью Closure Compiler и удаления мертвого кода, как видно на слайде, идентификатор this станет глобальным. Вы опять же получите ошибку в коде.

Примеры немножко утрированные, надеюсь, такие конструкции у себя в проектах вы не применяете. Тем не менее, есть более сложные случаи, когда advanced-оптимизации могут сломать ваш код. По ссылке вы можете посмотреть документацию Closure Compiler, какие ограничения он привносит на ваш код. Тем более нельзя гарантировать, что ваши внешние npm-зависимости после обработки Closure Compiler будут работать корректно.


Ссылка со слайда

Для нашего проекта это еще критичнее. Как я говорил, мы разрабатываем видеоплеер. Видеоплеер — это встраиваемая библиотека. Если Closure Compiler переименует поле в нашей встраиваемой библиотеке, переименует публичный метод, то внешний код сломается и не сможет взаимодействовать с плеером.

Наверное, как-то нужно сказать Closure Compiler о том, что какие-то поля можно минифицировать, какие-то нельзя, какие-то, очевидно, публичные, какие-то приватные.

Но выходит, что advanced-оптимизации в общем случае не безопасны. Их можно сделать безопасными, если вы используете Closure Compiler на полную мощность.

Я немножко слукавил. Closure Compiler — не просто минификатор, а целый комбайн. Он заменяет собой webpack, Babel и TypeScript. За счет чего и как ему это удается?


Ссылка со слайда

В Closure Compiler есть своя модульная система goog.provide, goog.require.


Ссылка со слайда

Есть своя транспиляция и вставка полифиллов для различных таргетов, для разных версий ECMASCRIPT.


Ссылка со слайда

Еще там есть свои аннотации типов. Только описываются они не как в TypeScript, а в JSDoc. Точно так же там можно пометить, например, модификаторы доступа public, private и подобные.

Если сравнивать микросистему webpack-Babel-TypeScript с Closure Compiler, то, на мой вкус, Closure Compiler проигрывает. У него чуть хуже документация, им умеют пользоваться меньше разработчиков. В целом не самый удобный инструмент.

Но я все-таки хочу оптимизации. Может, можно как-то взять Closure Compiler, взять TypeScript и объединить их?

Такое решение есть. Называется оно tsickle.


Ссылка со слайда

Это проект, который разрабатывается в Angular и занимается тем, что компилирует TypeScript-код в JS-код с аннотациями Closure Compiler.


Ссылка со слайда

Есть даже webpack loader, tsickle-loader называется, который внутри использует tsickle и заменяет собой tsickle loader. То есть он подгружает TypeScript-код в webpack и эмитит JavaScript с аннотациями. После чего можно запустить Closure Compiler как минификатор.

Проблема в том, что в README tsickle явно написано, что работа над проектом в процессе, и Google внутри его использует, но снаружи большого опыта использования этого проекта нет. И завязываться на отдельный компилятор от TypeScript не очень-то и хочется, потому что тогда вы будете отставать по фичам от TypeScript и не получать какие-то новые возможности.

Собственно, tsickle loader имеет ту же проблему, так как он основан на tsickle. В общем, это решение сейчас не выглядит как хорошее продакшен-решение.

Какие еще есть варианты? В TypeScript есть issue про предложение о минификации. Там идет обсуждение похожей реализации на Closure Compiler: оптимизировать код, используя знания о типах. Проблема в том, что этот issue открыт 15 июля 2014 года, там до сих пор ничего не происходит. Вернее, там происходит 145 комментариев, но результатов пока нет. Судя по всему, команда TypeScript не считает, что компилятор TypeScript должен заниматься минификацией. Это задача других инструментов.

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

Не так давно в Babel появилась поддержка TypeScript. Существует babel/preset-ypescript, который добавляет в Babel возможность парсить TypeScript-код и эмитить JavaScript. Он делает это путем удаления всех TypeScript-модификаторов.

Мы, кстати, не так давно перешли с ts-TS loader на Babel с использованием babel/preset-typescript и этим сильно ускорили сборку. Наконец-то настроили конкатенацию модулей в webpack, сделали еще некоторые оптимизации и настроили разные сборки под ES5- и ES6-браузеры. Про это можно узнать подробнее из доклада моего коллеги.

Окей, давайте попробуем написать babel-plugin, который будет минифицировать приватные поля, раз Babel умеет работать с TypeScript.


Ссылка со слайда

Как Babel работает? На эту тему есть много хороших материалов, статей и докладов. Можно начать, например, с этого материала на Хабре. Я лишь бегло расскажу, как происходит процесс обработки кода через Babel.

Итак, Babel, как и любой транспойлер кода, сначала парсит его и строит абстрактное синтаксическое дерево, abstract syntax tree, AST. Это некоторое дерево, которое описывает код.

Давайте попробуем на коротком кусочке кода посмотреть, как строится AST. Наша маленькая программа состоит из двух выражений. Первое выражение — это Variable Declaration, объявление перемены. Из чего оно состоит? Из оператора VAR — на схеме это Variable Declarator. У оператора есть два операнда — идентификатор A, мы создаем переменную A, и Numeric Literal, мы присваиваем им значение «3».

Второе выражение в программе — это Expression Statement, просто выражение. Оно состоит из бинарного выражения, то есть операции, у которой есть два аргумента. Это выражение «+», первый аргумент «a», второй «5», числовой литерал. Примерно так строятся абстрактные статические деревья.

Если вы хотите подробнее в это окунуться или когда-нибудь будете писать свой плагин для Babel, вам очень сильно поможет инструмент AST Explorer. Это онлайн-приложение, куда вы можете просто скопировать ваш код и посмотреть, как строится для него абстрактное синтаксическое дерево.

Здесь есть поддержка Babel и TypeScript. Очень удобный, практически незаменимый инструмент, чтобы заниматься написанием плагинов для Babel.


Ссылка со слайда

Когда мы построили AST, мы его трансформируем. Трансформация — это превращение одного AST в новое, измененное. Как происходит трансформация? Это тот самый процесс, который вы описываете с помощью настроек Babel в файлике .babelrc или где-то еще.

Вы задаете список плагинов, которые вы трансформируете в ваше AST дерево.

Что такое плагин? Это просто функция, которая имплементирует паттерн Visitor. Babel обходит AST в глубину, в том порядке, как указано на схеме на слайде. Babel вызывает функцию вашего плагина, который возвращает объект с некоторыми методами.

В зависимости от того, в каком узле мы сейчас находимся, вызывается соответствующий метод объекта, который вернул плагин. Для идентификатора вызовется Identifier, для строки вызовется StringLiteral и т. д.

Подробно об этом можно узнать из документации Babel и воспользоваться AST Explorer, чтобы понять, какие операции в коде каким узлам в AST- дереве соответствуют.


Ссылка со слайда

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

Давайте напишем плагин, который модифицирует приватные поля. Спойлер: у меня ничего не получилось. Расскажу, почему.

Как будет работать наш плагин? Давайте возьмем какой-нибудь класс, в котором есть приватное поле. Наш плагин Visitor будет заходить во все узлы AST-дерева, соответствующего классу, и в MemberExpression. Это узел, соответствующий операции доступа к полю в объекте.


Ссылка со слайда

Если объект, к которому мы обращаемся, this, то надо проверить, является ли поле приватным.


Ссылка со слайда

Для этого нужно подняться вверх по AST-дереву и найти декларацию этого поля. Тут нам повезло: она имеет модификатор private, значит, можно переименовать это поле.


Ссылка со слайда

Магия! Все работает, классно. Плагин готов.

Так я думал, пока не начал активнее его тестировать.


Ссылка со слайда

Рассмотрим пример. Тут мы добавили в наш класс еще один метод, внутри которого обращаемся к приватному полю не this, а другого инстанса этого класса. Это валидный код, мы обращаемся к приватному полю внутри класса, здесь все ок.

Но мой плагин при обработке этого кода делал вот такое. Почему так происходило? Потому что я сделал так, что мы ищем доступы к объекту this. Если же мы обращаемся к другому объекту, эти узлы AST-дерева мы не рассматриваем.

Окей, можно написать тут костыль, который будет рассматривать все MemberExpression и искать, а в данном случае — пытаться искать, поднимаясь вверх по AST-дереву, декларацию идентификатору foo. Здесь это легко, она описывается в теле функции, в заголовке функции. Можно понять, что у нее стоит тип foo, значит, это поле тоже нужно переименовать.

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


Ссылка со слайда

Рассмотрим еще один пример. Здесь мы this присваиваем переменную и обращаемся к полю bar из этой переменной. Это тоже валидный код. Но в итоге я получал такое. Здесь тоже нужен костыль, который будет разбирать такие обращения, искать, что this foo — на самом деле this, что у него тип foo. И в этом случае bar нужно точно так же переименовать.

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

Я расстроился и уже было похоронил эту идею.

Но потом я вернулся в тот долгий тред про минификацию в TypeScript и увидел там комментарий Евгения Тимохова.

Он писал, что реализовал TypeScript в трансформер, который занимается минификацией приватных полей. И этот трансформер вроде бы даже работает.

У Евгения немножко другой подход. В своем трансформере он не переименовывает поля полностью, как я хотел, а добавляет к ним префикс, нижнее подчеркивание private нижнее подчеркивание.

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

Почему трансформер Евгения работает, а подход, который выбрал я, ни к чему не привел?


Ссылка со слайда

Тут нам потребуется немножко поговорить о том, как работает TypeScript. TypeScript — это ведь тоже транспойлер. Он занимается тем, что берет TypeScript-код и парсит его. Но вместе с парсингом TypeScript еще запускает Type Checker. И при построении AST-дерева TypeScript обогащает его информацией о типах идентификаторов. Это как раз та самая информация, которой мне не хватало в моем Babel-плагине.

Дальше компилятор TypeScript может трансформировать AST-дерево точно так же, как Babel. Построить новое AST-дерево и эмитить из него JavaScript-код.

На этапе трансформации можно подключить кастомный трансформер. Это точно такой же плагин, точно такая же сущность, как плагин Babel, только для TypeScript. Она использует такой же паттерн Visitor и реализует примерно те же идеи.

Как это все использовать? Проблема в том, что в CLI компилятора TypeScript нет возможности подключать кастомные трансформации. Если вы хотите такое делать, вам потребуется пакет ttypescript.


Ссылка со слайда

Это не опечатка, а обертка над компилятором TypeScript, которая позволяет в настройках компилятора в tsconfig указать возможность использовать кастомную трансформацию. Здесь кастомная трансформация будет просто браться из node_modules.


Ссылка со слайда

Такая фича есть и в ts-loader. Там тоже можно задать функцию getCustomTransformers, которая позволит вам применить кастомный трансформер на этапе сборки.

Когда я решил попробовать этот подход, то столкнулся с проблемой. У нас-то в проекте используется Babel и babel/preset-typescript. Как вы помните из рассказа, мы на него переехали из ts-loader и получили кучу профита, сделали кучу оптимизаций. Откатываться обратно и терять все это мне не хотелось.

Окей, будем делать свой велосипед еще раз. Как выглядит сейчас пайплайн сборки в моем проекте? Мы подгружаем TypeScript-код в Babel loader и эмитим из него JS. Тут мне нужна сущность, которая перед Babel позволит запускать TypeScript-трансформер.

В Babel этого сделать нельзя, потому что он не запускает компилятор TypeScript. Как я говорил, он просто вырезает модификаторы TypeScript из кода.


Ссылка со слайда

Идею такой сущности я подсмотрел в проекте react-docgen-typescript-loader. Это такой loader для webpack, который пригодится, если вы используете Storybook. Storybook — инструмент, который позволяет строить визуальные гайды и документацию к вашим React-компонентам.

Чем занимается этот loader? Он подгружает TypeScript-код, обрабатывает его и эмитит TypeScript-код с дополнительными полями у React-компонентов. Поля называются docgenInfo, и в них содержится информация для Storybook, чтобы построить документацию к React-компоненту, используя не propTypes, а аннотации TypeScript.

Потом этот код, заэмиченный в react-docgen-typescript-loader, как-то обрабатывается. Например, с помощью TS loader и Babel. В итоге, когда он попадает в Storybook, тот успешно строит документацию по полям docgenInfo.

Мне нужна похожая штука. Мне нужен webpack loader. Как это сделать?


Ссылка со слайда

Webpack loader — это просто функция. Она принимает исходный код файла в виде строки и возвращает исходный код, тоже может его как-то модифицировать.

Здесь на слайде очень глупый loader, который занимается тем, что все ваши файлы превращает в код, содержащий console.log(«Hello World!»).


Ссылка со слайда

Loader может быть и синхронный. Можно получить callback, сделать асинхронную операцию. Вернее, что-то прочитать с диска или сделать что-то подобное и вызвать callback с новым модифицированным source.

Какой пайплайн должен быть у меня? Мне нужен loader, который подгружает TypeScript, запускает на нем кастомный трансформер. Я хочу применить трансформер, который минифицирует приватные поля и эмитит TypeScript.

Дальше я смогу его обработать с помощью Babel loader, как я делал это сейчас, и эмитить JS. А «нашлепка» из моего кастомного loader будет опциональной. Если что-то пойдет не так, я всегда смогу ее отключить, и максимум, что я здесь потеряю, — минификацию приватных полей. И это не потребует от меня перестройки всего остального пайплайна сборки.

Окей, мы разобрались, как писать loader. Функция, которая обрабатывает source. Теперь нужно понять, как применить на наш файл кастомную TypeScript-трансформацию.


Ссылка со слайда

Для этого нам потребуется TypeScript Transformation API — как раз тот самый программный API, который позволяет обработать исходный код на TypeScript, применить к нему трансформацию и заэмитить TypeScript-код. Это то, что нужно моему loader.

Примерно как это работает? Сначала нужно получить TS program, это объект, который содержит коллекцию файлов и настройки компилятора TypeScript. Нам нужно распарсить исходный файл и получить для него AST. Потом мы трансформируем это дерево и подключаем здесь myCustomTransformer, нашу кастомную трансформацию. И получаем в переменной result новое AST. Дальше мы его можем сериализовать обратно в TypeScript-код. Этим занимается компонент printer.

Кажется, ничего не мешает использовать это в webpack loader. Единственная проблема: документация по Transformation API не очень хорошая. И вообще, документация по внутренним сущностям компилятора в TypeScript сильно проигрывает аналогичной документации Babel. Но если вы захотите окунуться в это, начать можно с пул-реквеста в репозитории в TypeScript, где Transformation API сделали публичной.

Итак, что в итоге делает мой loader? Подгружает TypeScript-код и с помощью TypeScript Transformation API применяет на него кастомную трансформацию. Эмитит уже модифицированный TypeScript-код обратно. Дальше я скармливаю его Babel, который эмитит JavaScript.

Итоговую реализацию loader я выложил в npm, можно посмотреть исходный код на GitHub и даже подключить и использовать в вашем проекте:

npm install -D ts-transformer-loader

Всю эту прекрасную конструкцию мы даже покатили в продакшен.

Какой профит дала вся эта возня? Сырой непожатый код нашего бандла уменьшился на 31 килобайт, это почти 5%. Результаты в gzip и brotli не такие классные, потому что код и повторяющиеся идентификаторы там и так хорошо сжимаются. Но выигрыш — порядка 2%.

Уменьшение непожатого кода на 5% — не очень крутой выигрыш. Но 2% в минифицированном коде, который вы гоняете по сети, можно даже заметить на мониторингах.

Вот ссылка на мои заметки. Спасибо.

Let's block ads! (Why?)