Grafana: выборка в запросе диапазона дат из переменных

Если требуется обработать и показать данные по выбранному диапазону, то необходимо использовать в запросе переменные $__from и $__to. Однако стоит учитывать, что они в запросе встают как время с эпохи unix. А именно большое длинное число. Поэтому если например в БД у даты типа дата, без времени, то приходится использовать преобразование вида:

EXTRACT(EPOCH from agreements_dt)*1000

Чтобы из даты вида 2026-01-01 получить 1767214800000. Ну и соответственно в запросе:

select to_char(date_trunc('day', dt), 'YYYY-MM-DD'),count(*) as "Великоустюгское территориальное отделение" from (
                    select 
                            date_trunc('month', agreements_dt)::date as dt
                    from 
                            ls 
                    where EXTRACT(EPOCH from agreements_dt)*1000 between $__from and $__to and owner='000000003' and agreements_dt is not null
                    ) as agr
            group by dt 
            order by dt	asc

Max: Исключение повторных нажатий кнопок до ответа сервера

Как бы не ругались некоторые товарищи на Habr, что «разрабатывать под Max, себя не уважать», но тем не менее не все имеют этот выбор 😉 Так что проблемы решать таки нужно..

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

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

...
    if (isset($in->update_type)){
        if ($in->update_type=="message_callback"){
          $params=[];
          $params["callback_id"]=$in->callback->callback_id;
          $data=[];
          $data["message"]["attachments"]=[];
          $data["notification"]="Подождите ответа сервера...";
          $res=$Max->Reqwest("answers",$params,$data,"POST");    
        };
        if (($in->update_type=="message_created") or ($in->update_type=="message_callback") or ($in->update_type=="bot_started")){                       
          $res=$Api1c->reqwest("MaxCallback2",["body"=>$body]);  // пересылаем всё что пришло в 1С  и получаем ответ
          if ($res==null){
            $params=[];  
            $params["chat_id"]=$in->message->recipient->chat_id;
            $data=[];
            $data["text"]="Временная ошибка серверера. Попробуйте позже.";    
            $data["format"]= "html";            
                $buttons=[];                
                    $button=[];
                    $button["type"]="callback";
                    $button["text"]="Перейти в главное меню";
                    $button["payload"]="Go2MainMenu";                
                $buttons[]=$button;                                
                    $attachment=[];
                    $attachment["type"]="inline_keyboard";
                    $attachment["payload"]["buttons"]=[];
                    $attachment["payload"]["buttons"][]=$buttons;                                                     
            $data["attachments"]=[];    
            $data["attachments"][]=$attachment;
            $res=$Max->Reqwest("messages",$params,$data,"POST");    
            die();  
          };
    // если 1С вернула ошибку то её и показываем пользователю
          if ($res->error==true){
            $params=[];  
            if (isset($in->message)){$params["chat_id"]=$in->message->recipient->chat_id;};
            if (isset($in->chat_id)){$params["chat_id"]=$in->chat_id;};        
            $data=[];
            $data["text"]=$res->result;    
            $data["format"]= "html";            
                $buttons=[];                
                    $button=[];
                    $button["type"]="callback";
                    $button["text"]="Перейти в главное меню";
                    $button["payload"]="Go2MainMenu";                
                $buttons[]=$button;                                
                    $attachment=[];
                    $attachment["type"]="inline_keyboard";
                    $attachment["payload"]["buttons"]=[];
                    $attachment["payload"]["buttons"][]=$buttons;                                                     
            $data["attachments"]=[];    
            $data["attachments"][]=$attachment;
            
            $res=$Max->Reqwest("messages",$params,$data,"POST");          
            die();  
          };
          // Что пришло из 1С, то и отдаём в сообщения пользователю..
          $params=$res->result->params;
          $data=$res->result->data;
          $res=$Max->Reqwest("messages",$params,$data,"POST");          
          die();
...

Javascript: формирование csv файла

Задача: на чистом javascript сформировать и сохранить из браузера файл формата csv, адекватно открывающийся табличным редактором (например OnlyOffice)

Решение:

rows=[];
rows.push(["aa","bb"]);

let csvContent = "data:text/csv;charset=utf-8," + rows.map(e => e.join(",")).join("\n");
var encodedUri = encodeURI(csvContent);
var link = document.createElement("a");
link.setAttribute("href", encodedUri);
link.setAttribute("download", "download.csv");
document.body.appendChild(link); 
link.click();

Что тут происходит: заполняется некий массив, далее создаётся ссылка в DOM, и в элемент ссылки записываются данные массива. Далее эмулируется нажатие собственно на эту ссылку.

Nginx: настройка проксирования

Задача: есть локальный web сервис запущенный…ну например в докере. Не суть в общем где запущен. Может быть просто какой то локальный сервис типа web интерфейса к zigbee2mqtt: htpp://192.168.8.1:8080 Необходимо дать возможность открывать его в браузере, с нормальным ssl сертификатом ну и всё такое. Вот собственно для этого и нужно проксирование.

Решение: первым делом получим сертификат для домена. Для этого добавим в настройки nginx следующие строчки:

server {
        listen 80;
        server_name ваш_домен;
        root /var/www/m;
        index index.php;
}

После чего получим сертификат:

service nginx restart
certbot certonly --webroot -w /var/www/m -d ваш_домен

Далее настройки nginx изменим на:

server {
        listen 80;
        listen [::]:80;
        listen 443 ssl;
        server_name ваш_домен;
        if ($scheme = 'http') {return 301 https://$host$request_uri;}
        ssl_certificate     /etc/letsencrypt/live/ваш_домен/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/ваш_домен/privkey.pem;
        location / {
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            proxy_pass http://192.168.8.1:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Real-IP $remote_addr;
        }
}

Что тут есть?

  1. Переадрессация с http на https
  2. Подсовывание сертификата
  3. Проксирование http://192.168.8.1:8080 на ваш_домен

Собственно и всё

Получение ExecutorGUID и orgPPAGUID

При работе с ГИС ЖКХ это первоочередные идентификаторы, которые в самой панели ГИС ЖКХ спрятаны так, что не каждый с первого раза и найдёт. Поэтому есть способ чуть попроще.

  1. Авторизуемся на ГИС ЖКХ https://portal.dom.gosuslugi.ru/
  2. В браузере нажимаем F12 и переходим на вкладку «Сеть»
  3. В правом верхнем углу нажимаем на название своей организации -> посмотреть информацию об организации, и смотрим содержимое запроса из вкладки «Сеть»

guid в «полезной нагрузке» это и есть orgPPAGUID

Для получения ExecutorGUID, точно так-же нажимаем на название своей организации -> Посмотреть информацию о представителе организации. Ничего не меняя нажимаем «Сохранить». В сетевом запросе updateinfo, видим требуемый guid:

1 2 3 56