$ echo -n 'hello habr' | openssl enc -a -aes-256-ecb -K 4446323738323245364331303639424446323738434435393532324536333130
GoMz9XzmRJ3m5QKwxuIceA==
$ echo 'GoMz9XzmRJ3m5QKwxuIceA==' | openssl enc -a -aes-256-ecb -K 4446323738323245364331303639424446323738434435393532324536333130 -d
hello habr
$ echo -n DF27822E6C1069BDF278CD59522E6310 | xxd -pu -c 32
4446323738323245364331303639424446323738434435393532324536333130
[Unit]
Description=My Script Service
After=multi-user.target
[Service]
User=user # Имя пользователя, от имени которого будет запускаться команда, указанная в ExecStart
WorkingDirectory=/home/user/program # Директория, в которую будет осуществлён переход перед запуском
ExecStart=/home/user/program/venv/bin/python /home/user/program/webui.py
Restart=always # Всегда перезапускать скрипт при его падениях
[Install]
WantedBy=multi-user.target
/etc/systemd/system
с расширением .service
, например, /etc/systemd/system/my_program.service
.activate
для активации виртуального окружения, но вместо этого указывается полный путь к интерпретатору Python внутри этого виртуального окружения.WorkingDirectory
.Unit
в качестве After
задайте network-online.target
вместо multi-user.target
:After=network-online.target
sudo systemctl daemon-reload
sudo systemctl enable --now my_program.service
//Данные по содержанию меню
const furnTypes = [
{
'id' : 1,
'title' : 'Офисная <span>меб</span>ель'
},
{
'id' : 2,
'title' : 'Техника / сантехника'
},
{
'id' : 3,
'title' : 'Сборка кухни'
},
];
//Каждое активно может быть только один раз
const activeMenuItems = new Set();
const clickHandler = e => {
if(activeMenuItems.add(parseInt(e.target.getAttribute("furn-id"))) ) {
//Если поменялся перестроим меню
updateActiveMenuItems(activeMenuItems);
}
};
//Строим меню на основе данных items, вставляем в target
//на элементы вешаем событие clickHandler
buildSelectMenu = (target, items, clickHandler) =>
{
//создаем родителя
const parentUl = document.createElement("ul");
parentUl.classList.add('types-select__options');
//Каждый из пунктов
items.forEach(
item => {
const childLi = document.createElement("li");
childLi.classList.add('types-select__option');
childLi.setAttribute('furn-id', item['id']);
childLi.addEventListener("click",clickHandler);
const childTextBlock = document.createElement("div");
childTextBlock.classList.add('types-select__option-text');
// Лучше потом на setHtml заменить, но пока нет поддержки
childTextBlock.innerHTML = item['title'];
childLi.appendChild(childTextBlock);
parentUl.appendChild(childLi);
}
)
//Запихиваем туда куда нужно
target.appendChild(parentUl);
return parentUl;
}
const updateActiveMenuItems = ids => {
const parent = document.querySelector('.types-select__active-items');
parent.innerHTML = '';
ids.forEach( id => {
const curItem = furnTypes.find( el => el['id'] === id)
const activeMenuItemEl = document.createElement("div");
const activeMenuItemTitleEl = document.createElement("span");
activeMenuItemTitleEl.innerHTML = curItem['title'];
const activeMenuItemdeleteButtonEl = document.createElement("button");
//Лучше через текстовую ноду
activeMenuItemdeleteButtonEl.innerHTML = 'x';
activeMenuItemdeleteButtonEl.setAttribute('active-furn-id', curItem['id']);
activeMenuItemdeleteButtonEl.addEventListener("click", e => {
//Чтобы кликалась не взирая на клик событие родителя
if(e && e.stopPropagation) e.stopPropagation();
if(activeMenuItems.delete(parseInt(e.target.getAttribute("active-furn-id"))) ) {
//Если поменялся перестроим меню
updateActiveMenuItems(activeMenuItems);
}
});
activeMenuItemEl.appendChild(activeMenuItemTitleEl);
activeMenuItemEl.appendChild(activeMenuItemdeleteButtonEl);
parent.appendChild(activeMenuItemEl);
})
}
// Строим меню
const furnitureMenu = buildSelectMenu(
document.querySelector('#furnitureMenu'),
furnTypes,
clickHandler
);
document.querySelector('.types-select__btn')
.addEventListener("click", e => {
// Странное имя класса, наверное должн быть types-select__menu_active
document.querySelector('.types-select__menu').classList.toggle("_active");
} )
<link href="styles.css?random_string">
<link href="styles.css?<?=time()?>">
<link href="styles.css?<?=md5_file(путь_к_файлу/styles.css)?>">
<link href="styles.css?<?=filemtime(путь_к_файлу/styles.css)?>">
# History search with Up/Down keys.
if [[ $- == *i* ]]; then
bind '"\eOA": history-search-backward'
bind '"\e[A": history-search-backward'
bind '"\eOB": history-search-forward'
bind '"\e[B": history-search-forward'
fi
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.6-NqQiSlSYgbZ-x31JH6g17DkFiTu04VM6CPiptriB0
то можно заметить, что он состоит из трёх частей, разделенных точкой.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
— это заголовок. JSON-объект, закодированный в base64 строку, содержащий информацию о самом токене: тип и алгоритм шифрования, используемый для получения подписи. Если раскодировать строку, то получается {"alg":"HS256","typ":"JWT"}
.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
— это полезная нагрузка. JSON-объект, который был сформирован сервером (эмитентом) при создании токена. Точно так же как и заголовок является base64 стрококой. Если раскодировать, то в данном примере получается {"sub":"1234567890","name":"John Doe","iat":1516239022}
6-NqQiSlSYgbZ-x31JH6g17DkFiTu04VM6CPiptriB0
— это подпись. В данном примере подпись сформирована с помощью алгоритма HS256 (что указано в первой части токена) следующим образом: HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
jwt-secret
)
jwt-secret
. Секретный ключ неизвестен клиенту, поэтому он не может сам сформировать JWT с любыми данными. Точнее может, но не может к этим данным сделать валидную подпись, так как не знает секрет. Сервер же секрет знает, поэтому может вычислить подпись из первых двух частей токена и сравнить её с третьей частью.iat
(issued at) — время, когда токен был выдан. Или exp
(expiration time) — время, после наступления которого токен станет истёкшим. JWT библиотеки используют exp
и при валидации токена выдают ошибку, если указанное время уже прошло. xrandr --addmode VIRTUAL1 1920x1080
xrandr --output VIRTUAL1 --mode 1920x1080
x11vnc -rfbport 25901 -clip 1920x1080+1920+0 -forever
site.com/url#btn
window.addEventListener('load', function() {
if (location.hash=='#btn') {
console.log('Press btn');
}
})
let url = document.location.href + '#btn';