22 декабря 2022 26 декабря 2022 Python


Два способа цеплять обработчики событий startup и shutdown

@application.on_event("startup")
def create_start_app_handler():
    print("start_app")

@application.on_event("shutdown")
def create_stop_app_handler():
    print("stop_app")


application.add_event_handler(
    "startup",
    create_start_app_handler,
)
application.add_event_handler(
    "shutdown",
    create_stop_app_handler,
)

В данном виде указанные события будут обработаны по 2 раза


22 декабря 2022 27 декабря 2022 Linux


Управление dd образами производится в 4 операции

sudo losetup --partscan --find --read-only --show  file.dd 

# создаёт виртуальный Диск и отображает имя файла виртуального диска, в моём случае /dev/loop15
# список разделов можно посмотреть вот так sudo fdisk -l /dev/loop15

sudo mount /dev/loop15p2 /mnt

# монтирует второй раздел виртуальный диска в директорую /mnt/
# тут какие то работы с данными образами

sudo umount /mnt 

# отмонтировать

sudo losetup -d /dev/loop15 

# удалить виртуальный диск

 


16 декабря 2022 Python


В инфраструктуре Flask есть фреймворк flask_restful для реализации Rest API и последние несколько лет там начались проблемы с развитием, в результате чего появилось несколько форков. В частности я решил попробовать построить один проект на flask_restx.

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

С помощью RESTX я реализовал следующие API

  • авторизация на основе JWT токен
  • обновление JWT токена с помощью Refresh токента
  • получение и редактирование профиля пользователя
  • создание точки загрузки файла
  • загрука файла частями (chunks)
  • просмотр списка загруженных файлов
  • просмотр свойств файла по ID

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

Но если смотреть в целом, то приходится для каждой модели базы писать модель сериализации

А ещё больше напрягло то, что фреймворк просто не предлагает ни какого способа пагинации. То-есть, API легко отдаёт всю выборку, а для того чтобы отдать сред выборки в стиле Django, необходимо руками высчитать total и сломать весь красивый механизм marshal_with

Так же, нет интеграции со sqlalchemy, конечно можно найти способ

но как по мне конструкция ModelX.query.filter(**kwargs).all() выглядит так себе


24 ноября 2022 Всякое


Попал в руки это замечательный контроллер и ради профилактики решил заменить. Новую предлагают купить за 23 тысячи.

Но если разобраться то оказывается, там стоит обычный аккумулятор за 600 рублей от старенького Sony Ericson

https://aliexpress.ru/item/32738463692.html

https://ru.dhgate.com/product/4pcs-original-for-sony-us503759-a8h-battery/397887722.html


05 ноября 2022 25 января 2023 Linux Linux OpenVPN Docker


Если нужно быстро запустить VPN на каком то удалённом сервере то проще всего воспользоваться готовым решением https://github.com/kylemanna/docker-openvpn

Затем делаете сколько нужно VPN ключей с помощью этой команды

docker run -v $OVPN_DATA:/etc/openvpn --rm -it kylemanna/openvpn easyrsa build-client-full USERNAME nopass

выгружаете в хостовую машину

docker run -v $OVPN_DATA:/etc/openvpn --rm kylemanna/openvpn ovpn_getclient USERNAME  > USERNAME.ovpn

И всё, можно пользоваться

Чтобы в дальнейшем сохранилась возможность генерировать новые ключи необходимо восстанавливать значение export OVPN_DATA="ovpn-data-example" и использовать passkey использованный для генерации корневого сертификата

Посмотреть статус OpenVPN сервера можно вот так

docker exec -it funny_varahamihira ovpn_status

Если необходимо запустить VPN клиента на постоянной основе, например на ubuntu сервере, то необходимо сгенерировать ключ для этого сервера, скопировать его в директорию /etc/openvpn/client.conf

и разрешить автозапуск

stemctl enable openvpn@client

 


27 октября 2022 Nginx Angie


При сборке на свежей системе может возникнуть ошибка

./configure: error: the HTTP rewrite module requires the PCRE library.
You can either disable the module by using --without-http_rewrite_module
option, or install the PCRE library into the system, or build the PCRE library
statically from the source with Angie by using --with-pcre=<path> option.

Эта ошибка проявляется если сборщик не может найти библиотку перловых регулярных выражений, решается проблема легко

sudo apt install libpcre3-dev


 


10 октября 2022 СуБД awk


for t in $(echo \\dt | psql $BASE |grep public |awk '{print $3}'); do 
    echo "SELECT setval('$t"_id_seq"', (select max(id)+1 from $t), true);"; 
done | psql $BASE

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


31 августа 2022 Python


При определении LOGGING.formatters.[name].format можно использовать следующие переменные

  • args=()
  • created=1661945167.5855541
  • exc_info=None
  • exc_text=None
  • filename=views.py
  • funcName=get_context_data
  • levelname=INFO
  • levelno=20
  • lineno=585
  • module=views
  • msecs=585.5541229248047
  • msg=Rubricator
  • name=expand_settings
  • pathname=./expand_settings/views.py
  • process=2078012
  • processName=MainProcess
  • relativeCreated=8258.57162475586
  • stack_info=None
  • thread=140543658326976
  • threadName=uWSGIWorker4Core0

Из этих переменны можно составить форматированную строку

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '{levelname} [{asctime}] {name}:{filename} {lineno} {message}',
        },
    }
}

Разобраться можно в файле /usr/lib/python3.10/logging/__init__.py:1100


27 июля 2022 Nginx


Недавно подключил к одному сайту ещё несколько доменов и получил вот такое значение

server_name www.server-name.ru www.server-name.it server-name.it server-name.ru servername.ru www.servername.ru www.server-name.ae www.servername.ae;

после чего сделал nginx reload и сервер начал работать как обычно, но после перезагрузки всего сервера Nginx не запустился и вы давал вот такую ошибку

nginx: [emerg] could not build server_names_hash, you should increase server_names_hash_bucket_size: 32

суть ошибки в том, у nginx по умолчанию определён небольшой размер буфера для хранения и обработки server_name

если определить размер этого буфера через переменную server_names_hash_bucket_size в разделе http файла /etc/nginx/nginx.conf то сервер будет нормально перезапускаться и работать


19 июля 2022 СуБД awk


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

table_name=auth_users

data_base=breys

echo "
begin;

select count(1) from $table_name ;

create  table ttt as select distinct id x,  * from $table_name ;

alter table ttt drop  column x;

truncate table $table_name; 

insert into $table_name (select * from ttt );

drop table ttt ;

select count(1) from $table_name ;

end;
" | psql $data_base

Суть метода такова:

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

Если необходимо обработать все таблицы базы данных то скрипт можно использовать в цикле

for table_name in $(echo '\dt' | psql $data_base | awk '{print $3}'| grep -v ^$|sort ); do  

echo "
begin;

select count(1) from $table_name ;

create  table ttt as select distinct id x,  * from $table_name ;

alter table ttt drop  column x;

truncate table $table_name; 

insert into $table_name (select * from ttt );

drop table ttt ;

select count(1) from $table_name ;

end;
" | psql $data_base; 

done

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


29 апреля 2022 Python


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

таблица пользователей таблице связей
1 осёл
2 козёл
3 мишка
4 пятачёк
5 винипух
1 2
1 3
4 5

 

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

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

class Union(db.Model):
    __tablename__ = "user_unions"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)

    user1_id = db.Column(db.Integer, db.ForeignKey('users.id'), primary_key=True)
    user2_id = db.Column(db.Integer, db.ForeignKey('users.id'), primary_key=True)

    created = db.Column(db.DateTime(timezone=True), server_default=func.now())
    updated = db.Column(db.DateTime(timezone=True), onupdate=func.now())

    enable = db.Column(db.Boolean, default=False, nullable=False)

    def __repr__(self):
        if self.enable:
            return "союз с {} заключён".format(self.user2)
        return "союз с {} не подписан".format(self.user2)


class User(db.Model):
    __tablename__ = "users"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(40), unique=False, nullable=False)
    email = db.Column(EmailType, unique=False)

    def add_user(self, user):
        """ добавление союза с user"""
        union = Union(user1=self, user2=user)
        db.session.add(union)
        db.session.flush()

Всё будет прекрасно до тех пор пока не потребуется у объектов User обратиться к списку союзных пользователей. Дело в том, что метол обращения к списку союзников выглядит по разному для разныз пользователей. Если для того чтобы узнать список союзников осла неоходимо выбрать все вторые колонки в строка в которых в первой колонке номер строки осла, а чтобы выбрать союзников козла, необходимо выбрать все первые колонки в строках где вторая колонка равна номеру строки козла. Таки образом получается метод выбора союзников осла и козла будет разный, а это не красиво.

Но всё таки можно реализовать универсальный метод поиск союзников для каждого пользователя и в SQL виде он будет выглядеть вот так

select * 
  from users 
  where id in (
    select user2_id id  
      from user_unions 
      where user1_id = 1 
    union 
      select user1_id id 
        from user_unions 
          where user2_id =  1
    ) 

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

Sqlalchemy позволяет реализовать и такой достаточно сложный запрос с помощью объектного ORM и тогда метод выбора союзников пользователя будет выглядет следующим образом

class User(db.Model):
    __tablename__ = "users"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(40), unique=False, nullable=False)
    email = db.Column(EmailType, unique=False)

    created = db.Column(db.DateTime(timezone=True), server_default=func.now())


    def get_all_union_users(self):
        """выборка запросов на союз"""
        q1 = db.session.query(Union.user2_id.label("id"))
        q1 = q1.filter(Union.user1==self)
        q2 = db.session.query(Union.user1_id.label("id"))
        q2 = q1.filter(Union.user2==self)
        return User.query.filter(
            User.id.in_(q1.union(q2))
        )

    def get_union_users(self):
        """выборка подтверждённых союзников"""
        q1 = db.session.query(Union.user2_id.label("id"))
        q1 = q1.filter(Union.user1==self, Union.enable==True)
        q2 = db.session.query(Union.user1_id.label("id"))
        q1 = q2.filter(Union.user2==self, Union.enable==True)
        return User.query.filter(
            User.id.in_(q1.union(q2))
        )

Проверяем результат

# проверяем список союзников у каждого пользователя стандартным способом
for u in User.query.all():u, "союзники", u.unions.all()
... 
(Пользователь: осёл , 'союзники', [союз с Пользователь: козёл  заключён, союз с Пользователь: мишка  заключён])
(Пользователь: козёл , 'союзники', [])
(Пользователь: мишка , 'союзники', [])
(Пользователь: пятачёк , 'союзники', [союз с Пользователь: винипух  заключён])
(Пользователь: винипух , 'союзники', [])


# проверяем список союзников у каждого пользователя проверяем через универсальный метод
>>> for u in User.query.all():u, "союзники", u.get_union_users().all()
... 
(Пользователь: осёл , 'союзники', [Пользователь: козёл , Пользователь: мишка ])
(Пользователь: козёл , 'союзники', [Пользователь: осёл ])
(Пользователь: мишка , 'союзники', [Пользователь: осёл ])
(Пользователь: пятачёк , 'союзники', [Пользователь: винипух ])
(Пользователь: винипух , 'союзники', [Пользователь: пятачёк ])

 


27 апреля 2022 13 октября 2022 Всякое


По службе пришлось столкнуться с сервисом продажи доменов hugedomains.com, интересующий нас домен определялся с такими данными

Domain Name: CENTERSVET.COM
   Registry Domain ID: 2524336753_DOMAIN_COM-VRSN
   Registrar WHOIS Server: whois.namebright.com
   Registrar URL: http://www.NameBright.com
   Updated Date: 2021-08-26T18:12:19Z
   Creation Date: 2020-05-10T18:15:17Z
   Registry Expiry Date: 2023-05-10T18:15:17Z
   Registrar: TurnCommerce, Inc. DBA NameBright.com
   Registrar IANA ID: 1441
   Registrar Abuse Contact Email: support@namebright.com
   Registrar Abuse Contact Phone: 17204960020
   Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited
   Name Server: NSG1.NAMEBRIGHTDNS.COM
   Name Server: NSG2.NAMEBRIGHTDNS.COM
   DNSSEC: unsigned
   URL of the ICANN Whois Inaccuracy Complaint Form: https://www.icann.org/wicf/

и при переходе на домен перекидывало на предложение о покупке домена за 3695 долларов (на момент покупки это 276 430 рублей).

Покупка доменов на www.hugedomains.com

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

Так же из этих форумов стало известно, что hugedomains.com находится на одном гео.адресе с namebright.com, а hugedomains.com это сервис аренды доменов. То-есть там выстроена схема в которой пользователи покупают и используют домен на namebright.com, а когда домен освобождается (например, в случае когда хозяин забыл оплатить), домен передаются в hugedomains.com, а там работает автоматизированная система продажи домена. У домена настраивается редирект на hugedomains.com, там открывается предложение о покупке и форма ввода данных, включая номер карты и cvv код

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

После оплаты счёта, мы в своём кабинете на namebright.com получили полный контроль над доменом, заменили DNS сервера и домен работает. Остаётся лишь перенести домен к нашим хостерам


18 апреля 2022 27 апреля 2022 Всякое


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

оставил такой отзыв на сайте https://www.nic.ru/info/otzyvy/

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

с каждым случаем использования стоимость продления увеличивается
и домены купленные за 150 рублей сейчас можно продлить минимум за 1600 рублей

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

сам по себе сервис технически реализован крайне безграмотно и является сшитым из кусков разных технологий франкленштейном рунет, это какой то позор

причём нужно понимать, что стоимость услуги совершенно не соответствует реальности и является завышенной как минимум на 1000% процентов, у других хостеров регистрация и продление доменов стоят 200-250 рублей в год, и там не навязываются ни какие платные опции в виде скрытого whois или антивируса для домена

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

на счёт службы поддержки сервиса: самое главное что нужно знать про эту службу это то, что они заинтересованы запутать вас, запугать и не дать возможности перевести домены к нормальному провайдеру

в качестве небольшой иллюстрации работы сервиса опишу как я переводил домены с nic.ru к другому хостеру

по ссылке Услуги >     Мои домены >         Перенос доменов
открывается форма на которой можно заказать перенос доменов
необходимо ввести адреса доменов и заказать перенос

и вот у меня в первый раз страница зависла и висели 15 минут, затем пришлось перезагрузить страницу и повторить ввод данных

после повторного  ввода списка доменов, появилась ошибка что они не могут перевести 3 из 2 доменов, да именно 3 из 2

пришлось ещё обновить страницу и в третий раз вести список доменов и заказать перенос, с третьего раза форма обработала и появилось сообщение что ушло письмо с инструкцией

письмо пришло через 3 дня со следующим текстом

An English version of this message is contained below.
 

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

пришлось в 4 раз вводить список доменов для переноса, опять вылазила ошибка о невозможности переноса 2 доменов из 2, но после обновления сработало

пришла СМС с кодом и через 30 минут пришло письмо с инструкцией

я подтвердил перенос, получил AuthInfo-коды для доменов, ввёл коды на новом хостинге и получил письма с запросом подтверждения переноса и вроде бы перенос сработал, но это не точно

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

Услуги >     Мои домены >         Перенос доменов

опять ошибка с вводом списка доменов, опять обновление и повторный ввод и сообщение что ушло письмо с инструкцией и СМС

СМС пришла, а письмо не пришло и видимо не придётся в течении трёх дней, я попробовал сменить почтовый ящик но система не даёт создать заказ переноса так как уже есть созданный ранее заказ

получается, необходимо ждать три дня, затем я получу уведомление что заказ отменён и повторить процедуру


звонить в техподдержку безсполезно, так как
во первых, очень должно дозваниваться, подтверждать свои  учётные данные, затем вас переключают на "специалиста" который просит проверить папку спам или перенастроить почту (типичные отмазки) и заканчивается диалог тем, что они якобы выслали письмо и оно вот вот придёт

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


я считаю, что nic.ru и reg.ru это монопольные и мощенические конторы, которые взвинтили цены на доменные имена более чем на 1000%, которые ежегодно выкачивают миллиарды денег из госучреждений и частных компаний

 

орфографию, подчёркивающие эмоции от использования сервиса, сохраняю


18 апреля 2022 27 апреля 2022 Всякое


Идея за 300

Евгений Касперксий приходит в nic.ru и reg.ru и предлагает им дополнительную услугую добавления домена в белый список антивируса Касперского, например по 100 рублей за домен.

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

А мошенники из nic.ru и reg.ru заключают договор  с Касперским и добавляют на своих сервисах обязательную и не отключаемую платную (по 1000р) услугу поддержки Антивируса Касперский всем своим клиентам.

После чего стоимость продления услуги возрастает с 1600 рублей, до 2600 рублей


03 марта 2022 Linux mpd songs amq


После блокировки западных стриминговых платных платформ можно оживить устройство Songs AMQ с помощью MPD

На любом хостинг-сервере ставим mpd

apt install mpd

закачиваем музыку в /var/lib/mpd/music

меняем на неё права

chown -R mpd:nogroup /var/lib/mpd/music/

в файле конфигурации /etc/mpd.conf можно оставить только такие настройки

playlist_directory        "/var/lib/mpd/playlists"
db_file            "/var/lib/mpd/tag_cache"
log_file            "/var/log/mpd/mpd.log"
pid_file            "/run/mpd/pid"
state_file            "/var/lib/mpd/state"
sticker_file                   "/var/lib/mpd/sticker.sql"
user                "mpd"
bind_to_address        "0.0.0.0" # слушаем все вшешние адрес
port                "6600"
password                        "Z2022.02.24@read,add,control,admin"
input {
        plugin "curl"
}
audio_output {    
    type        "httpd"    
    encoder     "lame"          # кодек lame необходим для работы с Songs AMQ
    port        "8000"    
    bitrate     "256"             # повышенный битрейт
    format      "44100:16:1"    
}
filesystem_charset        "UTF-8"

перезапускаем mpd

service mpd restart

Полученную ссылку в виде http://хостинг-сервер:8000 прописываем в songsApp

Всё работает. Управлять можно через приложение GMPC, так же есть приложения управления mpd для Android. Для управления используем адрес [хостинг-сервер], порт 6600, пароль из опции password


20 января 2022 28 декабря 2022 Hardware


Индикаторы самодиагностики на материнской плате 820-2128-B

Светодиод Название Цвет Значение Необходимо нажать на DIAG_LED Указание на
Led 1 Sleep Красный Выключен; горит, когда компьютер находится в спящем режиме Да Спяший режим
Led 2 Standby (Trickle Pwr) Жёлтый вкл Да Работа от резервного источника питания * **
Led 3 CPU B Error Красный выкл Нет Процессор Б останавливается на IERR (ошибка инструкции)
Led 4 CPU A Error Красный   Нет Процессор A останавливается на IERR (ошибка инструкции)
Led 5 CPU B OT Красный выкл Нет Перегрев процессор Б
Led 6 CPU A OT Красный выкл Нет Перегрев процессор А
Led 7 GPU Present Зелёный вкл Да EFI настроил видеокарту
Led 8 Power Good Зелёный вкл Да Все силовые шины работают
Led 9 EFI Done Зелёный вкл Да EFI загружается

Это резервная мощность. Он не позволяет внутренней резервной батарее (или «аккумулятору PRAM») работать, если только питание переменного тока не отключается полностью, например, в случае короткого сбоя питания или отключения компьютера от сети для обслуживания или перемещения.          

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