Переезд

Гугл прислал письмо о том, что бесплатная версия G Suite прекратит работать с 1 июля 2022 года. И это послужило толчком для переезда блога с blogspot на другую платформу. После недолгих поисков подходящего движка для блога, я остановил свой выбор на Hexo (https://hexo.io).

Тему для блога я выбрал простенькую “light”, которую пришлось немного “допилить”. “Допиленная” тема получила название “light plus” и лежит тут: https://github.com/coolsoftware/hexo-theme-light-plus

В теме было сделано следующее:

  • Заголовок оформлен как блок.
  • В заголовок добавлена картинка.
  • Добавлен блок контактов с возможность выводить в нем ссылку на репозиторий github, контактный email и ссылку на сайт.
  • Добавлен блок “Избранные сообщения”.
  • В “Архив” добавлена навигация по годам, месяцам и пролистывание (кнопки “Вперед”, “Назад”).

Кроме работы над темой, пришлось разбираться с импортом записей из старого блога. Для миграции из blogger в hexo нашелся “hexo-migrator-blogger”. Однако, он сильно устарел, некоторые зависимости уже не устанавливаются. А кроме того, мне хотелось, чтобы сохранились все внешние ссылки на записи. Поэтому пришлось писать свой “мигратор”: https://github.com/coolsoftware/hexo-migrator-blogspot.

Чтобы сохранить ссылки на существующие посты нужно:

  1. В новом блоге сделать такую же структуру записей, что была в старом, т.е. /<год>/<месяц>/<заголовок>. _config.yml:

    permalink: :year/:month/:name/
    new_post_name: :year/:month/:title.md
  2. Прописать mod_rewrite-правила (для Апача):

    RewriteEngine on
    RewriteRule ^(/[0-9]{4}/[0-9]{2}/[^/]+).html$ $1/ [NC,NE,L,R=301]
    RewriteRule ^/([0-9]{4})/$ /archives/$1/ [NC,NE,L,R=301]
    RewriteRule ^/([0-9]{4})/([0-9]{2})/$ /archives/$1/$2/ [NC,NE,L,R=301]

В одну строку

Генерация случайной строки (например, пароля) в nodejs в одну строку:

(function(length,charset){return [...Array(length)].reduce(x=>x+charset[Math.floor(Math.random()*charset.length)],'')})(16,'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789');

Base64 -> hex (nodejs)

(function(b64){return Buffer.from(b64, 'base64').toString('hex');})('AQAB');

Base64url -> hex (nodejs)

(function(b64url){return Buffer.from(b64url.replaceAll(/[_-]/g, function(m){return '/+'['_-'.indexOf(m)];}), 'base64').toString('hex'); })('AQAB_A=');

String -> Base64 (nodejs)

(function(s){return Buffer.from(s).toString('base64');})('test');

String -> Base64url (nodejs)

(function(s){return Buffer.from(s).toString('base64').replaceAll(/[+/=]/g,m=>'-_'['+/'.indexOf(m)]||'');})('test');

SHA-256 Digest (nodejs)

crypto.createHash('sha256').update('12345').digest().toString('hex');

Переименование файлов: “Сериал.Part-1.mkv” -> “Сериал.S01E01.mkv”, “Сериал.Part-2.mkv” -> “Сериал.S01E02.mkv” и т.д. (bash)

for i in *.mkv; do mv $i $(echo $i | sed 's/Part-\([0-9]\)/S01E0\1/'); done;

ROT13 (“шифр Цезаря”)

(function(s){return s.split('').reduce((x,y) => x + ('nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'['abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.indexOf(y)] || y), '')})('Test12345');

Base64 to Pascal array of bytes:

(function(b64){return Buffer.from(b64, 'base64').reduce((s,b,i)=>s+(i?(i%16==0?',\n':', '):'')+'$'+b.toString('16').padStart(2, '0'), '')})('AQAB');

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

Mongo: Aggregation with Pagination

Допустим у нас есть такая коллекция users в Mongo DB:

[
{
username: "john.doe",
roles: ["user"],
contacts: [
{
name: "John Doe",
email: "johndoe@gmail.com"
},
{
name: "J.D.",
email: "jd@gmail.com",
phone: "+11111111111"
}
]
},
{
username: "jack.sparrow",
roles: ["manager"],
contacts: [
{
name: "Jack Sparrow (gmail)",
email: "jack.sparrow@gmail.com",
},
{
name: "Jack Sparrow (hotmail)",
email: "jack.sparrow@hotmail.com",
}
]
}
]

И мы хотим организовать просмотр всех контактов всех пользователей в одной таблице с разбивкой по страницам (“pagination”). То есть так:

Чтобы получить все контакты можно воспользоваться aggregation с $unwind.

Еще нам нужно посчитать количество всех контактов, после чего “вырезать” только те, которые нужно показать в выбранной странице. Для того, чтобы выполнить обе эти операции одним запросом, воспользуемся чудесной пайплайн-стадией $facet. Получится так:

db.collection('users').aggregate([
{"$unwind":"$contacts"},
{"$facet":{
"meta":[
{"$count": "totalCount"}
],
"data":[
{"$sort":{'contacts.name':1,'username':1}},
{"$skip":firstRow-1},
{"$limit":page_size},
{"$replaceRoot":
{"newRoot":{
"$mergeObjects":[
{
"username":"$username",
},
"$contacts"
]
}}
},
],
}},
]);

page_size - это количество записей на странице.

firstRow - это номер первой записи на странице. Вычисляется он так:

const firstRow = (page > 0 ? ((page-1) * page_size) : 0) + 1;

Вместо $unwind могут быть другие пайплайн-стадии, которые выбирают нужные для показа данные. Основной же прием, который я хотел продемонстрировать в этом посте, - это использование $facet.

Полный пример можно посмотреть на github:

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

ShellIconOverlayIdentifiers (windows 10)

Забавная вещь в Windows 10: ShellIconOverlayIdentifiers

О том что это такое можно почитать в документации Microsoft: https://docs.microsoft.com/en-us/windows/win32/shell/how-to-register-icon-overlay-handlers

А забавно тут вот что: есть лимит на количество “обработчиков наложения значков” (icon overlay  handlers), который равен 15. Выбираются первые (по алфавиту) 15 обработчиков, остальные игнорируются. Поэтому ушлые разработчики софта, которые желают, чтобы их обработчики шли первыми в списке и работали, начали добавлять в начале имени пробелы (см. скриншот). Кто больше пробелов добавит, тот и молодец :) Интересно, кто-нибудь уже додумался при установке  приложения проверять существующие обработчики на предмет наличия в них пробелов в начале, и делать в своих на один больше? 

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

Полезные ссылки: блоги

Полезные блоги:

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

MongoDB: aggregate

Пример aggregation в Mongo:

db.targets.aggregate([
{$sort:{"processing":1}},
{$lookup:{
from:"campaigns",
let:{campaign:"$campaignname"},
pipeline:[
{$match:{$expr:{$and:[
{$eq:["$campaignname","$$campaign"]},
{$eq:["$enabled",true]}
]}}}
],
as:"campaign"}
},
{$unwind:"$campaign"},
{$limit:1},
{$set:{
date:Date.now(),
processing:{
$ifNull:[{$add:["$processing",1]},1]
}
}},
{$merge:"targets"}
]);

Соответствующий UPDATE в MySQL выглядел бы примерно так:

update targets 
inner join (
select targets.id from targets
inner join campaigns
on targets.campaignname=campaigns.campaignname
where campaigns.enabled
order by processing
limit 1
) t
on targets.id = t.id
set processing=IFNULL(processing,0)+1, date=now()

Второй вариант короче, проще, понятнее, и напишу я его в разы быстрее. В общем, по-моему, Mongo - это сильно на любителей заморочиться…

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

Полезные ссылки (Best Practics)

Как-то я начал коллекционировать “Полезные ссылки“. В настоящий момент они разбиты на 10 тем:

Новая 11-я тема называется “Best Practices”:

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

Manage Web Site with Git: switch default branch

GitHub настоятельно рекомендует использовать не master а main. Не буду давать эмоциональную оценку этому, она очевидна…

В общем, есть последствие если git использовать для деплоя сайта/приложения на веб сервер.  Описание тут: Using Git to Manage a Live Web Site. Хук post-receive не будет работать. Точнее, не будет работать чекаут:

GIT_WORK_TREE=/path/to/webroot/of/mywebsite git checkout -f

Вылетает ошибка: fatal: You are on a branch yet to be born

Короче, надо сменить дефолтный бранч в bare репозитории на веб сервере с master на main, после чего все будет работать как надо:

cd mywebsite.git
git symbolic-ref HEAD refs/heads/main

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru