Архив рубрики: Павел

INSERT / UPDATE в MySQL на Python

Ну в принципе и не на Python, важен принцип. Что мне нужно? Если UPDATE завершился ошибкой (данные не обновлены т.к. их нет), тогда INSERT. Вообще в MYSQL есть специальная команда — REPLACE, у которой синтаксис такой-же как у INSERT, за исключением того, что если такая запись уже есть, то она просто измениться. Одно НО. Таблица обязательно должна содержать колонки с UNIQUE. А если их нет, и добавить нельзя? Тогда к сожалению придется «отлавливать» результат выполнения UPDATE — а именно количество изменившихся записей. Если 0 — считаем что такой записи вообще нет, и вставляем. На Python нечто подобное реализовать можно вот так:

sql="update device_status set param_value=0,stamp=now() where param_name='Uptime' and device_id="+str(device_id);   
 func.putlog("---обновляю статус свича в биллинге ")    
 cursor2 = connb2.cursor()
 func.putlog("----SQL:"+sql)    
 try:
  cursor2.execute(sql);
  connb2.commit();
  print (format(cursor2.rowcount))
  if format(cursor2.rowcount)=="0":
    func.putlog("---- новый свич, добавляем данные в таблицу..");
    sql="INSERT into device_status (record_id,device_id,port_id,param_name,param_value,stamp) values (null,"+str(device_id)+",null,'Uptime',UNIX_TIMESTAMP(),now())";
    cursor2.execute(sql);
    connb2.commit();  
    sql="INSERT into device_status (record_id,device_id,port_id,param_name,param_value,stamp) values (null,"+str(device_id)+",null,'State',2,now())";
    cursor2.execute(sql);
    connb2.commit();

1С Дополнительные реквизиты на форме при помощи расширения

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

1) Выносим в расширение документ документ «Реализация товаров и добавляем реквизит «ПокупкаССайта» в реквизиты формы. Да! именно в реквизиты формы, а не документа. В настоящий момент платформа 1С не дает добавлять в расширения реквизиты документа (но обещают). Перетаскиваем реквизит в элементы и обязательно ставим галочку на «АктивизироватьПоУмолчанию» — иначе на форме при запуске не отобразится:

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

  

И далее создадим обработчики открытия формы и закрытия в которых реализуем чтение из регистра и запись в этот регистр:

&НаСервере
Функция ЗаписатьЗначениеПокупкиССайта(параметр)	
		МенеджерЗаписи = РегистрыСведений.ДополнительныеСведения.СоздатьМенеджерЗаписи();
		МенеджерЗаписи.Объект = объект.Ссылка;
		МенеджерЗаписи.Свойство = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Продажа с сайта");		
		МенеджерЗаписи.Значение=параметр;
		МенеджерЗаписи.Записать();	
конецфункции

&НаСервере
Функция ПолучитьЗначениеГалочки();
	зн=РегистрыСведений.ДополнительныеСведения.СоздатьНаборЗаписей();
	отбор=зн.Отбор;
	отбор.Объект.Установить(объект.Ссылка);
	отбор.Свойство.Установить(ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Продажа с сайта"));		
	зн.Прочитать();	
	галочка=ложь;
	для каждого стр из зн   цикл
		галочка=стр.Значение;
	конеццикла;
	возврат галочка;
КонецФункции  	
&НаКлиенте
Процедура Расш1_ПровестиИзФормыПосле(Команда)
ЗаписатьЗначениеПокупкиССайта(этаформа.ПокупкаССайта);
КонецПроцедуры

&НаКлиенте
Процедура Расш1_ПровестиИЗакрытьИзФормыПосле(Команда)
	Расш1_ПровестиИзФормыПосле(Команда);
КонецПроцедуры

&НаКлиенте
Процедура Расш1_ПриОткрытииПосле(Отказ)
	этаформа.ПокупкаССайта=ПолучитьЗначениеГалочки();
КонецПроцедуры

Пачкой обновляем сертификаты SSL на сервере

Как известно сертификаты LetsEncrypt живут только 3 месяца, и соответственно их нужно регулярно обновлять. Самый хороший и простой способ обновления сертификатов — по записи DNS, однако это подходит не всем. Обновление же сертификата записью файла в «корне» сайта тоже не всегда работает по разным причинам. Выходом может стать следующий «финт ушами»: создаем альтернативный файл виртуальных хостов (рассматриваю вариант с apache, nginx практически не использую), который используется исключительно под получение сертификатов. Т.е. логика следующая: скрипт «подменяет» файл с виртуальными хостами на временный «облегченный», apache перестартовываем, спокойно обновляем сертификаты, затем возвращаем оригинальный файл с виртуальными хостами и снова перестартовываем apache. Особенностью «временного» файла является то что задана единая точка хранения файлов которые требуется создать для обновления сертификатов.

Итак получается скрипт обновления, что-то вроде:

«Временный» файл хостов:

<VirtualHost *:80>
    ServerAdmin pawfvwervwe@mail.ru
    DocumentRoot "/usr/local/www/apache24/sites/lets"
    ServerName ewfer.tv
    ServerAlias www.werfer.tv wer.ru werf.ru rwefwerf.ru # перечисляем все домены которые нужно обновить
    <Directory "/usr/local/www/apache24/sites/lets">
    Options Indexes FollowSymLinks MultiViews
        AllowOverride All
    allow from all
    Require all granted	
    </Directory>    
</VirtualHost>

bash скрипт обновления:

#!/bin/sh
echo "- Подменяю текущий конфиг виртуальных хостов apache"
cp /usr/local/etc/apache24/extra/httpd-vhosts.conf /usr/local/etc/apache24/extra/httpd-vhosts.conf.backup
cp /usr/local/etc/apache24/extra/httpd-vhosts.conf.lets /usr/local/etc/apache24/extra/httpd-vhosts.conf
/usr/local/etc/rc.d/apache24 restart
echo "- Получаю сертификаты"
certbot certonly --agree-tos --email rfer@mail.ru --webroot -w /usr/local/www/apache24/sites/lets/ -d refwer.tv
certbot certonly --agree-tos --email erferf@mail.ru --webroot -w /usr/local/www/apache24/sites/lets/ -d www.ewferf.tv
echo " - Возвращаю конфигурацию виртуальных хостов как было"
cp /usr/local/etc/apache24/extra/httpd-vhosts.conf.backup /usr/local/etc/apache24/extra/httpd-vhosts.conf
/usr/local/etc/rc.d/apache24 restart

 

Да, при таком способе получается некий простой в работе сайтов. Но раз в 3 месяца, ночью это для меня приемлимый вариант.

Как сделать простейшее API для своего проекта?

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

В моем случае сервер защищен от «чужих» протоколом SSL и сертификатом .p12 с паролем. Соответственно для получения данных по API на сторонних ресурсах необходимы будут pem и key файлы от выданного пользователю сертификата.

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

На сервере в корне создадим файл api.php:

<?php

/* 
 * (с) 2011-2019 Грибов Павел
 * http://грибовы.рф * 
 * Если исходный код найден в сети - значит лицензия GPL v.3 * 
 * В противном случае - код собственность ГК Яртелесервис, Мультистрим, Телесервис, Телесервис плюс * 
 */

/* Этот файл - API для получения из NOC различных данных в формате json
 * вход: post/get с заполненым параметром command (массив)
 * выход: json с результатом
 */

$version="1.0.0";


define('WUO_ROOT', dirname(__FILE__));

// Загружаем первоначальные настройки. Если не получилось - выходим
$rez = @include_once(WUO_ROOT.'/config.php');
if ($rez == false) {die();}

header('Content-Type: application/json; charset=utf-8');


// Загружаем классы
include_once(WUO_ROOT.'/class/sql.php'); // Класс работы с БД
include_once(WUO_ROOT.'/class/config.php'); // Класс настроек
include_once(WUO_ROOT.'/inc/connect.php'); // Соединяемся с БД, получаем $mysql_base_id
include_once(WUO_ROOT.'/inc/config.php'); // Подгружаем настройки из БД, получаем заполненый класс $cfg
include_once(WUO_ROOT.'/inc/functions.php'); // Загружаем функции

$command=_GET("command");
if ($command==""){
    $command=_POST("command");
};
if ($command!=""){
	if (is_file(WUO_ROOT."/api/".$command.".php")) {
		include_once(WUO_ROOT."/api/".$command.".php");
	} else {
            $ret["result"]=false;
            $ret["message"]="Метод/команда не найдена";
            die(json_encode($ret));
	}    
} else {
  $ret["result"]=false;
  $ret["message"]="Метод/команда не задан";
  die(json_encode($ret));
};

?>

Как мы видим — суть его, поймать на входе команду, и выполнить файл из папки /api/, совпадающий с именем команды. Так мы избавляемся от головной боли с раширением количества команд API.

В папке /api/  создадим первую команду нашего API — файл version:

<?php

  $ret["result"]=true;
  $ret["message"]="$version";
  die(json_encode($ret));
  
  ?>

Теперь пример использования данного API (для удобства обернуто в класс):

<?php

class NocApi {    
    var $curl_opts = array(
        CURLOPT_URL=>"https://куц.укамцук.ru/api.php",
        CURLOPT_RETURNTRANSFER => true, 
        CURLOPT_SSL_VERIFYPEER => false, // вход по SSL
        CURLOPT_SSL_VERIFYHOST => false,
        CURLOPT_FOLLOWLOCATION => false, // редиректы
        CURLOPT_MAXREDIRS => 10, // максимальное количество редиректов
        CURLOPT_CONNECTTIMEOUT=>5,
        CURLOPT_CONNECTTIMEOUT=>5,
        CURLOPT_VERBOSE=>true,
        CURLOPT_SSLCERT=>"/home/уцм/укм/cert/укмцу.crt",
        CURLOPT_SSLKEY=>"/home/уцкму/цукм/cert/цукм.key",
        CURLOPT_SSLKEYPASSWD=>"123",
        CURLOPT_POST=>true,
        CURLOPT_POSTFIELDS=>"command=version"
    );
    
    public function __construct(){
        if (! function_exists('curl_init')) {
            throw new Exception('CURL модуль для PHP не установлен!');
        }
    }
    public function Exec($runc=""){
        $curl = curl_init();        
        foreach ($this->curl_opts as $opt => $val){
            curl_setopt($curl, $opt, $val);        
        };
        if ($runc!=""){
          curl_setopt($curl, CURLOPT_POSTFIELDS, $runc);  
        };
        $data = curl_exec($curl);
        if (curl_errno($curl)) {
              $ret["result"]=false;
              $ret["message"]=curl_error($curl);
              die(json_encode($ret));            
        } else {
            return json_decode($data);
            curl_close($curl);
        };
    }
    
}

$noc=new NocApi();
$res=$noc->Exec("command=version");
var_dump($res);

Результат выполнения:

object(stdClass)#2 (2) { ["result"]=> bool(true) ["message"]=> string(5) "1.0.0" }