Flutter: основные принципы построения приложения

Минимальное приложение Flutter, это:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
debugShowCheckedModeBanner: false, //убираем надпись debug
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),

    );
  }
}

,далее обычно начинают плясать именно от этого шаблона. Запускаем, смотрим…и ничего кроме квадратика с названием окна не видим. Ну собственно ничего более и нет.

Одной из «киллер фич» Flutter считается, что всё что визуально расположено на экране — это виджеты вложенные друг в друга. С «детьми» и «родителями». Принцип разметки примерно такой же как в HTML + CSS: кидаем в «стартовый» виджет другие виджеты различного вида , и добавляем к ним «украшения». В данном случае внутри стартового виджета ничего нет.

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

Добавляем файл-основной экран page1.dart:

import 'package:flutter/material.dart';
class Page1 extends StatefulWidget {
  _Page1State createState() => _Page1State(); // сюда передаем текущее состояние страницы
}
class _Page1State extends State<Page1> { // _ впереди класса, означает чтоб скрыть доступ из другх файлов
  @override
  Widget build(BuildContext context) {
    return Scaffold( // чаще всего используемый "корневой" виджет, который позволяет добавлять в себя другие: AppBar, BottomNavigationBar,Drawer, FloatingActionButton и т.п.
        appBar: AppBar(title: Text("Home page")),
        body: Center(
            child: Text(
              "Ууу как всё запущено пациент..",
              textAlign: TextAlign.center,
              style: Theme.of(context).textTheme.headline3,
            )
        )
    );
  }

}

И далее добавляем в main,dart строчки:

В результате запуска получаем что-то типа:

Установка и настройка Flutter

За сим открываю цикл статей по использованию платформы для разработки кросс платформенных приложений с использованием Flutter и соответственно языка Dart.

Итак, минимальный старт для начала разработки следующий:

  1. Устанавливаем Dart
  2. Устанавливаем Android Studio
  3. Устанавливаем Flutter
  4. Настраиваем Android Studio для работы с Flutter и Dart

1. Установка Dart

Заходим на https://dart.dev/get-dart/archive , скачиваем последний релиз, распаковываем в папку.

2. Скачиваем, устанавливаем Android Studio.

3. Заходим на https://docs.flutter.dev/get-started/install, скачиваем и распаковываем последний релиз Flutter

Настройка Flutter пока заключается в том, чтобы зайти в папку E:\flutter\, и запустить из командной строки

flutter_console.bat
flutter doctor

Вы должны увидеть что-то типа:

Visual Studio — может использоваться вместо Android Studio. На Android Studio скорее всего у вас будет «крестик», т.е. он не настроен. Собственно далее необходимо перейти к п.4 — настройка Android Studio

4. Настройка Android Studio

Первым делом необходимо установить плагины: File -> Settings -> Plugins

После чего, перезагрузите Android Studio и снова зайдите в консоль Flutter:

flutter_console.bat
flutter doctor --android-licenses

И снова заходим в Android Studio и создаем проект на Flutter, предварительно выставив где находится flutter и dart:

Будет создан каркас приложения с минимальным функционалом для запуска. Выберем целевую платформу, и запустим

RabbitMQ: Отложеная доставка и время жизни пакета

Собрал наконец в одном классе, всё что мне нужно от RabbitMQ: а именно отправка сообщения в очередь, с отложеной доставкой, плюс уничтожение сообщения по прошествии какого то времени «не доставки».

Ну да, казалось бы элементарные вещи, но на PHP примеров для этого случая не сказать что уж слишком много как оказалось..

<?php

//Необходимые классы
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Wire\AMQPTable;


class TRabbit extends TLog{ 
    public $host="";
    public $login="";
    public $password="";
    public $port="";
    public $connection=null;
    public static $isInstance=false; 
    // настройки касающиеся очереди
    public $passive=false;      // может использоваться для проверки того, инициирован ли обмен, без того, чтобы изменять состояние сервера
    public $durable=true;       // убедимся, что RabbitMQ никогда не потеряет очередь при падении - очередь переживёт перезагрузку брокера
    public $exclusive=false;    // используется только одним соединением, и очередь будет удалена при закрытии соединения
    public $autodelete=false;   // очередь удаляется, когда отписывается последний подписчик
    public $ttl=3000;           // время жизни пакета, через которое оно при недоставке дропнется,в милисекундах
            
    public function __destruct(){
        if (self::$isInstance==false) {        
          $this->connection->close();  
        }
    }    
    public function __construct(){
        self::$isInstance=true;
        $this->host= TConfig::GetConfigKeyValue("rabbitmq_host");
        $this->login= TConfig::GetConfigKeyValue("rabbitmq_login");
        $this->host= "localhost";
        
        $this->password= TConfig::GetConfigKeyValue("rabbitmq_password");
        $this->password="guest";
        $this->port= TConfig::GetConfigKeyValue("rabbitmq_port");        
        $this->connection = new AMQPStreamConnection($this->host, $this->port, $this->login, $this->password);        
    }    
    /**
     * Поставить в очередь сообщение
     * @global type $db
     * @param type $queue   - имя очереди
     * @param type $message - сообщение в json формате
     * @param type $delay   - отсрочка доставки в секундах (необязательно)
     * @param type $ttl     - время жизни пакета, через которое оно при недоставке дропнется
     * @return type
     */
    public function  Producer($queue,$message,$delay=0,$ttl=null) {  
     global $db;
        try {    
          $answer = AnswerStrucNext(false, "Failed to queue a RabbitMQ message");             
             $channel = $this->connection->channel();      
            $channel->exchange_declare(
                'delay',
                'x-delayed-message',
                false,            /* не создается очереь если нет её */
                $this->durable,   /* гарантированная доставка */
                false,            /* удаление очереди если пустая и нет слушателей */
                false,            /* internal */
                false,            /* не ждать ответа от сервера (т. е. продолжать получение) */
                [
                    'x-delayed-type' => ['S', 'direct']                    
                ]);
             if (is_null($ttl)) $ttl=$this->ttl;
             $channel->queue_declare($queue, $this->passive, $this->durable, $this->exclusive, $this->autodelete,false,
                array(
                        "x-message-ttl" => array("I", $ttl)
                    )                     
                );
             $channel->queue_bind($queue, "delay", $queue);
               if ($delay==0){
                  $msg = new AMQPMessage($message);		        
               } else {
                   $msg = new AMQPMessage($message, ['delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT]);		
                   $msg->set('application_headers', new AMQPTable(['x-delay' => $delay*1000]));        
               };
          $channel->basic_publish($msg,"delay",$queue);                  
          $channel->close();
          $answer = AnswerStrucNext(false, "ok");             
       } catch(Exception $err) {
           $this->InsertLog(TLog::PT_PLAIN,"Ошибка постановки в очередь $queue (RabbitMQ)", TLog::R_Error, $message);
       };          
     return $answer;
    }
}

PHP: работа с файлами Excel

Одним из вариантов работы с файлами Эксель является библиотека PhpSpreadsheet.

Установка:

composer require phpoffice/phpspreadsheet

Пример использования:


/require_once('vendor/autoload.php');
 
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\{Font, Border, Alignment};
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;

$oSpreadsheet = IOFactory::load("akt.xlsx"); // шаблон с шапкой

$oSpreadsheet->getProperties()
   ->setCreator('SSK, OOO')
   ->setLastModifiedBy('SSK, OOO')
   ->setTitle('Акт сверки с клиентом') 
   ->setSubject('Акт сверки с клиентом')
   ->setKeywords('кцмукмц ')
   ->setCategory('узщшамыуозк ')
;

$oSpreadsheet->setActiveSheetIndex(0);
$wSheet = $oSpreadsheet->getActiveSheet();

$wSheet->setCellValueByColumnAndRow(1, 3, "Клиент: " . $res->result->user);
$wSheet->setCellValueByColumnAndRow(1, 4, "Дата с: " . $res->result->from);
$wSheet->setCellValueByColumnAndRow(1, 5, "Дата по: " . $res->result->from);

$totalrow=8;

$balance=0;
foreach($res->result->table as $row) {
    $source="";
    if ($row->doc_reservation>0){$source="уцкацук м №".$row->doc_reservation;};
    if ($row->doc_charge>0){$source="ы укмыу №".$row->doc_charge;};
    if ($row->doc_payment>0){
        $source="уфыкмыукм №".$row->doc_payment;
        if ($row->connector>0){$source=$source.",фыумукау №".$row->connector;};        
    }; 
    
    $wSheet->setCellValueByColumnAndRow(1, $totalrow, $row->date);    //дата
    $wSheet->setCellValueByColumnAndRow(2, $totalrow, $source);    // источник     
    $wSheet->setCellValueByColumnAndRow(3, $totalrow, $row->debit);    // дебет
    $wSheet->setCellValueByColumnAndRow(4, $totalrow, $row->credit);    // кредит
    $wSheet->setCellValueByColumnAndRow(5, $totalrow, $row->balance);    // баланс
    $wSheet->setCellValueByColumnAndRow(6, $totalrow, $row->ezs);    // ЭЗС
    $balance=$row->balance;
    $totalrow++;
}

$wSheet->setCellValueByColumnAndRow(1, $totalrow, "Текущий баланс: " . $balance);
$wSheet->getStyle('A'.$totalrow)->applyFromArray([
    'font' => [
      'name' => 'Arial',
      'bold' => true,
      'italic' => false,
      'underline' => Font::UNDERLINE_DOUBLE,
      'strikethrough' => false,
      'color' => [
          'rgb' => '808080' 
        ]
    ],
    'borders' => [
        'allBorders' => [
            'borderStyle' => Border::BORDER_THIN,
            'color' => [
                'rgb' => '808080'
            ]
        ],
    ],
    'alignment' => [
        'horizontal' => Alignment::HORIZONTAL_CENTER,
        'vertical' => Alignment::VERTICAL_CENTER,
        'wrapText' => true,
    ]
]);
// установить цвет фона ячейки
$wSheet->getStyle('A'.$totalrow)->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setARGB('00FF7F');
 

$oWriter = IOFactory::createWriter($oSpreadsheet, 'Xlsx');
$oWriter->save('php://output');

Логирование и аудит потенциально опасных событий на сервере Linux

Задача: при изменении критически важных файлов, запуск из под sudo, создание тоннелей SSH и переброс портов — отсылать уведомление об этом на почту.

Решение: воспользуемся штатным демоном auditd и сторонней утилитой wazuh

Установка auditd:

apt install auditd

Просмотр текущих действующих правил:

auditctl -l -a never,task

Стереть все действующие правила:

auditctl -D

1) Настроить уведомление об изменении файлов /etc/paswd и /etc/shadow

auditctl -w /etc/passwd -p wa -k passwd_watch
auditctl -w /etc/shadow -p wa -k shadow_watch

, где -p — обозначает при каком условии будет сделана запись в журнал:

  • r — операция чтения данных из файла
  • w — операция записи данных в файл
  • x — операция исполнения файла
  • a — операция изменения атрибутов файла или директории

Что бы правила работали на постоянной основе, их нужно добавить в файл /etc/audit/rules.d/audit.rules после чего перезапустить демон

systemctl restart auditd.service

2) Настройка уведомления о выполнении команд от sudo/root:

auditctl -a exit,always -F arch=b64 -F euid=0 -S execve -k rootcmd
auditctl -a exit,always -F arch=b32 -F euid=0 -S execve -k rootcmd

3) Для того чтобы уведомления из журнала /etc/audit/audit.log отправлялись на почту, нужно установить wazuh-manager:

add-apt-repository ppa:openjdk-r/ppa
curl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH | apt-key add -
echo "deb https://packages.wazuh.com/4.x/apt/ stable main" | tee -a /etc/apt/sources.list.d/wazuh.list
apt-get update
apt-get install wazuh-manager
systemctl daemon-reload
systemctl enable wazuh-manager
systemctl start wazuh-manager

И соответственно добавить в конфигурационный файл /var/ossec/etc/ossec.conf, отслеживание изменений:

<localfile>
    <location>/var/log/audit/audit.log</location>
    <log_format>audit</log_format>
  </localfile>

В этом же файле нужно настроить секцию касающуюся отправки уведомлений по email — отправитель, получатель.

Далее добавляем ключевые слова в /var/ossec/etc/lists/audit-keys, для фильтрации событий:

passwd_watch:read
shadow_watch:read
sudo:read

И добавить правило срабатывания в /var/ossec/etc/rules/local_rules.xml:

<group name="audit">
  < <rule id="100002" level="12">
      <if_sid>80700</if_sid>
      <match>sudo</match>
      <options>alert_by_email</options>
      <description>Выполнена команда от SUDO сервер sdcsdcezs.sadcasd.ru</description>
  </rule>
  <rule id="100003" level="12">
      <if_sid>80701</if_sid>
      <match>passwd_watch</match>
      <options>alert_by_email</options>
      <description>Изменился файл /etc/passwd</description>
  </rule>
</group>

1 70 71 72 73 74 300