Подготовка сервера RED OS 7.3 к установке Битрикс «Управление сайтом»

Дальнейший ход установки предполагает, что у вас уже установлена RED OS в конфигурации программ «Сервер минимальный», без выбора ПО (т.е. php, apache и т.д. будем ставить позже и сами!)

Отключим SELinux:

echo 'SELINUX=disabled' > /etc/sysconfig/selinux
reboot

Настроим отправку почты из консоли:

yum install sendmail –y

Для проверки работы:

echo "Это тестовое письмо " | mail -s "Проверка отправки почты " -r no-reply@mail.ru vasya@mail.ru

Установим PHP 8.1

dnf install php81-release 
dnf clean all
dnf makecache 
dnf  install php php-cli php-common php-devel php-gd php-imap php-json php-ldap php-mbstring php-mysqlnd php-opcache php-pdo php-pear php-pear-DB php-pecl-apcu php-pecl-mcrypt php-pecl-memcache php-pecl-ssh2 php-process php-pspell php-xml php-zipstream php-json php-xml

Если вдруг необходима версия PHP 7.4, которая идёт по умолчанию, то соответственно первая строчка не нужна

Установим Apache и Ngnix

dnf install httpd nginx

Установим MariaDB

dnf install mariadb-server mariadb

Установим Redis

dnf install redis 

Настройка Ngnix

Создадим папку /var/www/html/bx-site, присвоим ей пользователя и группу:

mkdir /var/www/html/bx-site
chown nginx:apache /var/www/html/bx-site

Далее качаем этот архив, и содержимое папки /redos/nginx переписываем поверх в /etc/nginx. Кроме того нужно в /etc/hosts добавить строчку:

127.0.0.1 httpd push

В случае необходимости включить сразу ssl, добавим в файл /etc/ngnix/sites-available/default.conf секцию вида:

server {

    listen 443 ssl;
    server_name _;
    server_name_in_redirect off;

    #ssl on;
    ssl_certificate /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/cert.key;


    proxy_set_header    X-Real-IP        $remote_addr;
    proxy_set_header    X-Forwarded-For  $proxy_add_x_forwarded_for;
    proxy_set_header    Host $host;

    proxy_redirect ~^(http://[^:]+):\d+(/.+)$ $1$2;
    proxy_redirect ~^(https://[^:]+):\d+(/.+)$ $1$2;

    set $docroot                "/var/www/html/bx-site";

    index index.php;
    root "/var/www/html/bx-site";

    # BXTEMP - personal settings
    include conf.d/bx_temp.conf;

    # Include parameters common to all websites
    include conf.d/bitrix.conf;

}

Если необходима (а она скорее всего необходима) переадрессация с http на https, то в секцию c 80 портом, добавляем строчки:

server {
    listen 80 default_server;
...
  if ($request_uri !~* "/robots.txt") {
        rewrite ^(.*)$ https://$host$1 permanent;
    }
...

Запустим сервис:

systemctl --now enable nginx

Настройка PHP

В папке /etc/php.d необходимо отредактировать настройки модулей:

opcache

opcache.max_accelerated_files = 100000
opcache.revalidate_freq = 0

bitrix.ini

display_errors = Off
error_reporting = E_ALL
error_log = '/var/log/php/error.log'
; Set some more PHP parameters
enable_dl = Off
short_open_tag = On
allow_url_fopen = On
# Security headers
mail.add_x_header = Off
expose_php = Off

Настройка Apache

  • Добавить в /etc/httpd/conf.d файл default.conf из архива выше
  • в httpd.conf заменить Listen 80 на Listen 8090
  • заменить файл /etc/httpd/conf.modules/00-mpm.conf на файл из архива выше

Запустить httpd:

systemctl --now enable httpd

На этом моменте файл index.php размещенный в /var/www/html/bx-site уже должен отображаться адекватно

Настройка MariaDB

В файле /etc/my.cnf.d/mariadb-server.cnf внутри тега [mysqld] необходимо добавить/изменить строки:

[mysqld]
transaction-isolation = READ-COMMITTED
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
thread_cache_size = 4

Запуск сервиса:

systemctl --now enable mariadb

Заходим в консоль сервера mysql, создаём пользователя и даём ему права:

create user 'test'@'localhost' identified by 'password';
create database testdb;
grant all on testdb.* to 'test'@'localhost'

Настройка Redis

В папке /etc/redis необходимо заменить файл redis.conf из архива выше, и выполнить в консоли:

usermod -g apache redis
chown root:apache /etc/redis/ /var/log/redis/
[[ ! -d /etc/systemd/system/redis.service.d ]] && mkdir /etc/systemd/system/redis.service.d
echo -e '[Service]\nGroup=apache' > /etc/systemd/system/redis.service.d/custom.conf
systemctl daemon-reload
systemctl --now enable redis

На этом в принципе и всё. Сервер подготовлен для развертывания битрикса в папке /var/www/html/bx-site. По идее далее кидаем туда дистрибутив (ну или бекап, если сайт переносите как я) и начинаем установку из браузера

Создаём нейросеть на Python

Ну вот реально, очень мало в интернете статей на тему использования нейросетей. Теоретической информации — полно. «Напишем нейросеть в 9 строчек» — полно. А вот практических примеров с разжевыванием — единицы. Одна из хороших статей тут на хабре. Начало отличное, конец скомканный и до конца не раскрытый. Кроме того код во многих статьях датируемых до 20г уже не рабочий, т.к. используют Tensorflow версии меньше 2. Или ранние 2 версии. Посему на написание живого примера потратил довольно таки много времени.

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

» Читать далее

Пишем игру Питон на Python ;). Часть 3

Продолжаем начатое. Добавим в игру увеличение скорости если питон ест, и подсчет очков. Добавим переменные:

...
speed=500;          # начальная скорость питона
increase_speed=10;  # размер увеличения скорости при удачной еде
score=0;            # набранные очки в результате игры
score_color=(100,100,50)  # цвет очков
...

Интересный момент работы с таймером. По умолчанию time.time() отдает данные в секундах целые числа, и доли секунд в цифрах после запятой. Для того чтобы соответственно удобно считать миллисекунды, значения нужно умножать на 1000. Изменим код:

start_time=time.time()*1000
...
while running:
    if (time.time()*1000-start_time)>speed:
        start_time=time.time()*1000
        Draw_foods(screen)
        Draw_python(screen)
...

А теперь изменим функцию Draw_python, добавив отрисовку количества очков, а так-же увеличение скорости и количества очков при поедании:

...
global score,speed
..
        for element in foods:
            if [element[0],element[1]] == head_coor:
                del_tail=False
                foods.remove(element)
                speed=speed-increase_speed  # увеличиваем скорость питона
                score=score+element[0]      # увеличиваем очки в зависимости от типа сожранного
...
 # Рисую набранные очки
    pygame.draw.rect(screen,(0,0,0),(width_field,0,width_field+300,100))
    img = font.render(f'Очки: {score}', True, score_color)
    screen.blit(img, (width_field+10, 20))
...

Добавим еще штрих — сделаем реакцию на столкновение со стенами поля, а именно чтоб питон переходил на левый край при столкновении с правым и т.д.,

...        if (head_coor[0]>size_x):
            head_coor[0]=1;
        if (head_coor[1]>size_y):
            head_coor[1]=1;
        if (head_coor[0]<0):
            head_coor[0]=size_x;
        if (head_coor[1]<0):
            head_coor[1]=size_y;
...

Теперь не хватает последнего: проигрыша в случае столкновения головы с хвостом. Объявим глобальный цикл в котором будет крутится игра, в нём разместим цикл игры. При проигрыше выводим вопрос «Вы проиграли хотите еще (Y/N)» Если игрок выбирает Y, то из глобального цикла не выходим, иначе покидаем глобальный цикл.

gloop=True
while gloop: # глобальный цикл
    pygame.init()
    ...
    font = pygame.font.SysFont(None, 24)
    while running: # цикл игры
        if (time.time()*1000-start_time)>speed:
            start_time=time.time()*1000
...
 # здесь оказываемся когда проиграли...
    pygame.draw.rect(screen,(200,100,5),(50,50,width_field-50,100))
    img = font.render(f'Вы проиграли. Сыграем еще партию? (Y/N)', True, score_color)
    screen.blit(img, (100, 80))
    pygame.display.flip()
    pygame.display.update()
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_y:
                    moved_direction_x = 0
                    moved_direction_y = 0
                    speed=500
                    score=0
                    gloop=True
                    running=False
                if event.key == pygame.K_n:
                    gloop=False
                    running = False
print("нормально вышли")

Окончательную версию игры можно скачать здесь

Токсичность сообщества ru.stackoverflow.com

Ежики кололись, плакали но продолжали есть кактус (с), или токсичность сообщества программистов на ru.stackoverflow.com

Уже несколько раз замечал, что в ru сообщества, просто нельзя ни задавать вопросы, ни отвечать на них — минусуют, хамят, лелеют и тешат чувство собственной значимости

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

Pixi.js: игра «жизнь»

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

У каждого жителя есть возраст,пол и профессия.

  • Жители двигаются по всему полю хаотично
  • При достижении возраста 120 лет житель умирает
  • Если два жителя сталкиваются между собой и они разного пола от 18 до 50 лет, тогда они рожают ребенка

Сначала определим объекты карты:

// Люди
class People {
    /**
     * Конструктор
     * @param {type} gender - пол
     * @param {type} age - возраст
     * @param {type} health - здоровье
     * @param {type} profession - профессия из map professions
     * @returns {People}
     */
  constructor(gender,age,health,profession) {
      this.gender=gender;           // пол 0/1 м/ж
      this.age=age;                 // возраст
      this.health=health;           // здоровье
      this.profession=profession;   // профессия      
  }  
}
class Citizen {
    constructor(name,pers,color) {
      this.name=name;
      this.pers=pers; // изначальныый процент от общего колиества
      this.color=color;
    }
    
}
// Зданиям
class Building {
    /**
     * Конструктор
     * @param {type} name   название
     * @param {type} cnt    количество зданий на 1000 жителей
     * @returns {Building}
     */
    constructor(name,color,cnt) {
      this.name=name;
      this.color=color;
      this.cnt=cnt;
    }
    /**
     * Установить начальные координаты
     * @param {type} x
     * @param {type} y
     * @param {type} height
     * @param {type} width
     * @returns {undefined}
     */
    setSpawn(x,y,height,width){
      this.x=x;  
      this.y=y;
      this.height=height;
      this.width=width;        
    }
}

Зададим переменные для игры:


/**
 * Профессии, они же модели поведения
 * @type Map
 */
professions=new Map(
        [
            // индекс, название,  % от всего населения
            [1,new Citizen("Детсадовец",10,0xFFFF0B)],    // ночует дома, днём ходит в детский сад
            [2,new Citizen("Школьник",10,0xAA0000)],      // ночует дома, днем до 13:00 в школе, потом идёт домой через магазин
            [3,new Citizen("Студент",10,0xFFFFFF)],       // ночует дома, днем до 17:00 в институте, потом идёт домой через магазин
            [4,new Citizen("Продавец",20,0xFF0000)],      // ночует дома, днём в магазине, потом идёт домой через магазин
            [5,new Citizen("Офисный клерк",20,0xFF3300)], // ночует дома, днём в офисе, потом идёт домой через магазин
            [6,new Citizen("Рабочий",20,0x0000FF)],       // ночует дома, днём на заводе, потом идёт домой через магазин
            [7,new Citizen("Пенсионер",10,0xDE3249)]      // ночует дома, днём шляется по магазинам или по улицам
        ]
);
buldings_types=new Map(
        [   
            [1,new Building("Жилище",0xDE3249,10)], // индекс, название, количество на 1000 жителей
            [2,new Building("Школа",0xAA0000,1)],
            [3,new Building("Университет",0xFFFFFF,1)],
            [4,new Building("Магазин",0xFF0000,2)],
            [5,new Building("Офис",0xFF3300,3)],
            [6,new Building("Завод",0x0000FF,1)],                
            [7,new Building("Детсад",0xFFFF0B,1)]                
        ]
);


// ну пусть размеры пока в экран, потом можно поиграться
var scrollWidth=GetScrollWidth();
var screen_width = window.innerWidth-4; //получаем ширину экрана
var screen_height = window.innerHeight-4; // получаем высоту экрана

residents_count=1000;   // начальное количество жителей
residents=[];           // массив жителей
buldings=[];            // массив зданий
cur_seconds=0;          // текущая секунда
cur_minuts=0;           // текущая минута
cur_hour=0;             // текущая час
cur_day=0;              // текущая день
cur_month=0;            // текущая месяц
cur_year=0;             // текущая год

max_FPS=60;

Напишем процедуры генерации зданий и жителей:

function GenerateNewCitizen(){
   x=randomIntFromInterval(1,screen_width-width);
   y=randomIntFromInterval(1,screen_height-height);      
   resident=new People(randomIntFromInterval(0,1),1,randomIntFromInterval(1,100),professions.get(1));
   
   graphics = new PIXI.Graphics();           
   graphics.beginFill(professions.get(1).color);
   graphics.lineStyle(2, professions.get(1).color, 1);
   graphics.beginFill(professions.get(1).color, 1);           
   graphics.drawCircle(0,0, 1);  
   graphics.position.set(x, y);         
   graphics.direction=randomIntFromInterval(0,360);
   graphics.endFill();        
   graphics.resident=resident;
   residents.push(graphics);

   app.stage.addChild(residents[residents.length-1]); 
    
}

/**
 * Начальная генерация зданий
 * @returns {undefined}
 */
function InitGenerateBuilding(){
  for (let i=1;i<= buldings_types.size;i++){
      cnt=buldings_types.get(i).cnt/1000*residents_count;
      yet_cnt=1;
      while (yet_cnt<=cnt){
        // случайным образом генерируем координаты и размеры здания
        width=randomIntFromInterval(20,60);
        height=randomIntFromInterval(20,60);
        x=randomIntFromInterval(1,screen_width-width);
        y=randomIntFromInterval(1,screen_height-height);      
        // если наложений нет, то добавляю здание в массив
        if (CheckingOverlaysRect(x,y,width,height,buldings)==false){
                console.log("Добавляю "+buldings_types.get(i).name+" №"+yet_cnt);
                graphics = new PIXI.Graphics();  
                graphics.lineStyle(2, buldings_types.get(i).color, 1);
                graphics.drawRect(x, y, width, height);
                buldings.push(graphics); 
            app.stage.addChild(buldings[buldings.length-1]);            
                FPSText = new PIXI.Text(buldings_types.get(i).name,new PIXI.TextStyle({fontFamily: 'Arial',fontSize: 11,}));
                FPSText.x = x;
                FPSText.y = y;            
            app.stage.addChild(FPSText);         
            yet_cnt++;
        };
      }
  };  
}

/**
 * Проверка наложения квадратов друг на друга
 * @param {type} x
 * @param {type} y
 * @param {type} width
 * @param {type} height
 * @param {type} rects
 * @returns {res|Boolean}
 */
function CheckingOverlaysRect(x,y,width,height,rects){
  res=false;
    for (let bi=1;bi<= rects.length;bi++){
         rect_height=rects[bi-1].getBounds().height;
         rect_width=rects[bi-1].getBounds().width;
         rect_x=rects[bi-1].getBounds().x;
         rect_y=rects[bi-1].getBounds().y;
         //console.log(rect_height,rect_width,rect_x,rect_y);
         if ((x>=rect_x)&(x<=rect_x+rect_width)){res=true;};
         if ((y>=rect_y)&(y<=rect_y+rect_height)){res=true;};
         if ((x+width>=rect_x)&(x+width<=rect_x+rect_width)){res=true;};
         if ((y+height>=rect_y)&(y+height<=rect_y+rect_height)){res=true;};         
    };  
  return res;
}

/**
 * Начальная генерация жителей
 * @returns {undefined}
 */
function InitGeneratePeoples(){
        
    for (let i=1;i<= professions.size;i++){ //перебираем все профессии
        console.log("-генерирую "+professions.get(i).name);
        for (let cnt=1;cnt<= professions.get(i).pers*residents_count/100;cnt++){
          x=randomIntFromInterval(1,screen_width-width);
          y=randomIntFromInterval(1,screen_height-height);      
          resident=new People(randomIntFromInterval(0,1),randomIntFromInterval(1,100),randomIntFromInterval(1,100),professions.get(i));


           graphics = new PIXI.Graphics();           
           graphics.beginFill(professions.get(i).color);
           graphics.lineStyle(2, professions.get(i).color, 1);
           graphics.beginFill(professions.get(i).color, 1);           
           graphics.drawCircle(0,0, 1);  
           graphics.position.set(x, y);         
           graphics.direction=randomIntFromInterval(0,360);
           graphics.endFill();        
           graphics.resident=resident;
           residents.push(graphics);

        app.stage.addChild(residents[residents.length-1]); 
          
          
          
        }
    }    
}

Создадим сцену и запустим время:

const app = new PIXI.Application({ 
    width:screen_width,
    height:screen_height,
    background: '#1099bb' ,
    antialias: true
});
document.body.appendChild(app.view);

app.stage.hitArea = app.screen;

InitGenerateBuilding(); // генерируем здания
InitGeneratePeoples();  // генерируем жителей

let ticker = PIXI.Ticker.shared;   
ticker.autoStart = false;
ticker.maxFPS=max_FPS;
ticker.start();

const style = new PIXI.TextStyle({
    fontSize: 20 
});
FPSText = new PIXI.Text('',style);
FPSText.x = 10;
FPSText.y = 10;   
app.stage.addChild(FPSText);         
StatText = new PIXI.Text(residents.length,style);
StatText.x = 10;
StatText.y = 30;   
app.stage.addChild(StatText);         


// пусть жители оживут!
ticker.add(function (time) {     
    cur_seconds++;
    if (cur_seconds==61){
        cur_seconds=0;cur_minuts++;
        // прибавляем всем жителям по году жизни..
        for (let i = 0; i < residents.length; i++) {         
            residents[i].resident.age++;
            if (residents[i].resident.age==120){
                residents.pop(residents[i]); // в 120 лет жизненный путь завершается..
            };
            
        };
    };
    if (cur_minuts==61){cur_minuts=0;cur_hour++;};
    if (cur_hour==25){cur_hour=0;cur_day++;};    
    if (cur_day==366){cur_day=0;cur_year++;};       
    FPSText.text="С начала эпохи прошел: "+cur_minuts+" год"; // выводим FPS
    StatText.text="Жителей:"+residents.length;
    // перебираю каждого человека и двигаем его
    for (let i = 0; i < residents.length; i++) {         
        step=false;
        while (step==false){
            pre_y=residents[i].position.y+Math.sin(residents[i].direction);
            pre_x=residents[i].position.x+Math.cos(residents[i].direction);
            if ((pre_x>=0)&& (pre_x<=screen_width) && (pre_y>=0) && (pre_y<=screen_height)) {
                    step=true;
            } else {
               residents[i].direction=randomIntFromInterval(0,360); 
            };
            
        };
       residents[i].position.y=residents[i].position.y+Math.sin(residents[i].direction);
       residents[i].position.x=residents[i].position.x+Math.cos(residents[i].direction);
       // проверим, есть ли совпадение точек?
       // если возраст от 18..50
       // если полы противоположные
       // то считаем что это "лябовь" и размножаемся
        for (let j = 0; j < residents.length; j++) {   
            if (Math.round(residents[i].position.y)==Math.round(residents[j].position.y) && 
                    Math.round(residents[i].position.x)==Math.round(residents[j].position.x) && i!=j){
                      if (residents[i].resident.age>=18 && residents[i].resident.age<=50){
                        if (residents[j].resident.age>=18 && residents[j].resident.age<=50){
                            if (residents[j].resident.gender==0 && residents[i].resident.gender==1){
                                console.log("-это лябофь!");  
                                GenerateNewCitizen();
                            }
                        }                          
                      }               
            };
        };
    }
});
1 6 7 8 9 10 73