diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..17c416b
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+ "python.analysis.extraPaths": [
+ "./app/source"
+ ]
+}
\ No newline at end of file
diff --git a/app/__init__.py b/app/__init__.py
index 0bb69cb..5ea3790 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -1,28 +1,23 @@
#!env/bin/python3.10
# -*- coding: UTF-8 -*-
-#активация FLASK
-#from flask import Flask
+# активация FLASK
+from flask import Flask, send_from_directory
-
-from flask import Flask, request, send_from_directory
-
-from flask import jsonify #для генерации JSON, не актуально
+from flask import jsonify # для генерации JSON, не актуально
from flask_cors import CORS
-#from flask import redirect, url_for
+# from flask import redirect, url_for
-#Активируем сжатие данных
+# Активируем сжатие данных
from flask_compress import Compress
compress = Compress()
app = Flask(__name__, static_url_path='', static_folder='')
compress.init_app(app)
-
-
-#Статика
+# Статика
@app.route('/favicon.ico')
def favicon():
@@ -34,29 +29,29 @@ def favicon():
def send_from_statics(path):
return send_from_directory('static', path)
+
@app.route('/temp/')
def send_from_temp(path):
return send_from_directory('temp', path)
-
+
# enable CORS
CORS(app)
# sanity check route
+
+
@app.route('/ping', methods=['GET'])
def ping_pong():
return jsonify('pong!')
-
-#@app.route('/')
-#def static_file(path):
+
+# @app.route('/')
+# def static_file(path):
# return app.send_static_file(path)#
-
-#активируем файл конфигурации
-app.config.from_object('config')
-#активируем Вьювер
+
+# активируем файл конфигурации
+app.config.from_object('config')
+
+# активируем Вьювер
from app import views
-
-
-
-
diff --git a/app/pages/rustdesk/config.ini b/app/pages/rustdesk/config.ini
new file mode 100644
index 0000000..b0300b7
--- /dev/null
+++ b/app/pages/rustdesk/config.ini
@@ -0,0 +1,29 @@
+[ACCESS]
+# Проверка интерфейса пользователя
+# 1 Администратор
+# 2 Оператор
+# 3 Врач-эксперт
+# 4 Агент
+# 5 Специалист
+# 6 Сортировщик
+# 7 Диспетчер
+# 8 Страховой представитель
+# 9 Сотрудник МФЦ
+# 10 Ведущий специалист
+# Пример: UserInterFace = 1,10 (без пробелов между запятыми)
+UserInterFace = 0
+
+# Минимальный уровень доступа пользователя от 0
+# 0 - без проверки
+# 1 - чтение
+# 2 - чтение и запись
+# 3 - полный доступ
+AccessOMS = 0
+AccessLPU = 0
+AccessUOG = 0
+AccessENP = 0
+AccessSMS = 0
+AccessPostCard = 0
+AccessPhone = 0
+AccessDial = 0
+AccessAnkets = 0
diff --git a/app/pages/rustdesk/script.js b/app/pages/rustdesk/script.js
new file mode 100644
index 0000000..04826ef
--- /dev/null
+++ b/app/pages/rustdesk/script.js
@@ -0,0 +1,41 @@
+// Автор: ELForcer
+// Дата оптимизации: 21.04.2023
+const RootComponent = {
+ data() {
+ return {
+
+ //Цвета
+ M_Red: false,
+ M_Green: false,
+ M_Blue: false,
+ M_Yellow: false,
+
+ //Модальные переменные
+ ModalTitleThread: "",
+ ModalTitle2: "Запуск",
+ ModalBodyText2: "Пожалуйста, подождите...",
+ ModalBodyThread: "",
+ ThreadMessage: "",
+ ThreadVars: {
+ message: '',
+ HTML: "",
+ Started: false,
+ },
+ }; //return
+ }, //data
+
+ //===========================================================================
+ methods: {
+ //-------------------------------------------------------------------------
+
+
+ }, // END metods
+
+ //===========================================================================
+ mounted() {
+ ThreadVars = this.ThreadVars;
+ }, // mounted()
+}; // Vue
+
+//Монтируем Vue как vueapp начиная с тега #vueapp
+vueapp = Vue.createApp(RootComponent).mount("#vueapp");
diff --git a/app/pages/rustdesk/source.py b/app/pages/rustdesk/source.py
new file mode 100755
index 0000000..aedc025
--- /dev/null
+++ b/app/pages/rustdesk/source.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Главное меню
+Дата последней оптимизации: 21.04.2023
+"""
+
+from app import app # чтение из config.py
+from flask import request # получение данных Cookie, GET и POST
+import sys
+import os
+import json
+
+# Подключаем свои библиотеки
+sys.path.append("app/source")
+sys.path.append("app/pages")
+import API_Common as APIC
+
+# Глобальные Переменные
+CurPath: str = "app/pages"
+NameModule = "rustdesk"
+
+
+###############################################################################
+# MAIN
+###############################################################################
+def Main(SessionID):
+
+ # Объявление переменных
+ Var = {"Title": "Главное меню", "ProgName": app.config['PROGNAME']}
+ Modal_Vars = {}
+
+ ###
+ TemplateData = ""
+ ScriptBody = ""
+
+ # Генерируем HTML
+ TemplateName = os.path.join(str(CurPath), str(NameModule), 'template.htm')
+ if (os.path.exists(TemplateName) == True):
+ with open(TemplateName) as fp:
+ TemplateData = fp.read()
+ fp.close()
+
+ # Подгружаем ява скрипт для вставки его в тело шаблона
+ ScriptName = os.path.join(CurPath, NameModule, 'script.js')
+ if (os.path.exists(ScriptName) == True):
+ with open(ScriptName) as fp:
+ ScriptBody = fp.read()
+ fp.close()
+
+ # Подставляем переменные в HTML
+ TemplateData = TemplateData = TemplateData.replace('}}', '}}')
+ TemplateData = TemplateData.replace('{{head}}', APIC.UserHead(Var)) # Подключаем раздел head со скриптами и css
+ TemplateData = TemplateData.replace('{{UserHeader}}', APIC.UserHeader(SessionID, Var)) # Рисуем шапку на странице
+ TemplateData = TemplateData.replace('{{Modals}}', APIC.Modals(Modal_Vars)) # Модальные окна
+ TemplateData = TemplateData.replace('{{ThreadVars_message}}', APIC.ThreadVars_Message()) # Сообщения от потока
+ TemplateData = TemplateData.replace('{{version}}', app.config['VERSION']) # Версия Сервера КМС-ИК
+ TemplateData = TemplateData.replace('{{IPServer}}', request.host.split(":")[0]) # Версия Сервера КМС-ИК
+ TemplateData = TemplateData.replace('{{ScriptBody}}', ScriptBody) # Скрипты. Например, Vue.JS
+
+ return TemplateData # Отправляем обработанный шаблон
diff --git a/app/pages/rustdesk/template.htm b/app/pages/rustdesk/template.htm
new file mode 100644
index 0000000..c7de33d
--- /dev/null
+++ b/app/pages/rustdesk/template.htm
@@ -0,0 +1,84 @@
+
+
+
+
+ {{head}}
+
+
+
+
+ {{UserHeader}}
+
+ 1. Зайти и скачать дистрибутив с сайта RustDesk или скачать:
+
+
+
+ 2. Напротив ID нажать на троеточие;
+ 
+ 3. Выбрать "Сеть"
+ 
+ 4. Выбрать "Нажать "Разблокировать сетевые настройки"
+ 
+
+ Может потребоваться ввод пароля администратора
+ 
+
+
+
+
+
+
+
+
+ 7. Вернитесь в закладку "Главная" и сообщите ваш ID. Дождитесь подключения к Вашему рабочему столу.
+
+ 
+
+
+ Запрос на подключение будет выглядеть так:
+
+ 
+
+
+ Принятое подключение будет выглядеть так:
+
+ 
+
+
+
+
+
diff --git a/app/source/API_App.py b/app/source/API_App.py
new file mode 100644
index 0000000..cf94457
--- /dev/null
+++ b/app/source/API_App.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3
+# -*- coding: UTF-8 -*-
+"""
+Нестандартные процедуры, которые использует только это приложение
+Дата последней оптимизации: 07.06.2022
+"""
+import uuid # Уникальные имена
+import os # Работа с ОС
+import shutil # Копирование файлов
+import time # работа с временем
+import datetime # Работа с датой
+import sys
+
+from app import app # чтение из config.py
+
+sys.path.append("app/source")
+import API_Common as APIC
+
+
+NameModule = "API_App"
+
+###############################################################################
+def UserHeader(SessionID: str, Vars: dict) -> str:
+ """Вызывается из API_Common"""
+ # print(str(Vars["request"].url.split('/')))
+ request = Vars["request"]
+ CurStr = Vars["CurStr"]
+
+ Link = ""
+ LinkReport = ""
+
+ return CurStr
diff --git a/app/source/API_Common.py b/app/source/API_Common.py
new file mode 100644
index 0000000..701024a
--- /dev/null
+++ b/app/source/API_Common.py
@@ -0,0 +1,1176 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+API с общими функциями и процедурами
+Дата последней оптимизации: 22.05.2022
+Дата обновления: 26.01.2024
+"""
+
+import subprocess
+import uuid # Генератор GUID
+import datetime # Работа с датой
+import time # работа с временем
+import os # Работа с файловой системой
+import requests # Работа с http/s
+
+# Для Криптографии
+import hmac
+import base64
+import hashlib
+
+import socket # Для проверки доступности портов
+from app import app # чтение из config.py
+from flask import request # Для получения метаданных о запросе
+
+import sys # Подключаем свои библиотеки
+sys.path.append("app/source")
+import API_App
+
+appname = app.config['SHMNAME']
+NameModule = "API_Common"
+
+
+################################################################################
+class DataSession:
+ """Класс для передачи данных о сесссии"""
+
+ def __init__(self):
+ self.status: bool = False # Действует сессия или нет
+ self.comment: str = ""
+ self.sessionid: str = ""
+ self.userid: str = ""
+ self.username: str = ""
+ self.stationid: str = ""
+
+
+################################################################################
+def HTML_Progress(Color, IsAnimated: bool, PCur: int, PMax: int, MainDiv: bool = True) -> str:
+ """
+ Создание строки прогресса для HTML
+ :param Color: error, warning, blue, info, success
+ :param IsAnimated: True или False
+ :param PCur: Любое целое число
+ :param PMax: Любое целое число
+ :return: str
+ """
+ ProgressColor = ""
+
+ if (Color.lower() == "red" or Color.lower() == "danger" or Color.lower() == "error"):
+ ProgressColor = "bg-danger"
+
+ if (Color.lower() == "yellow" or Color.lower() == "warning"):
+ ProgressColor = "bg-warning"
+
+ if (Color.lower() == "green" or Color.lower() == "success"):
+ ProgressColor = "bg-success"
+
+ if (Color.lower() == "info"):
+ ProgressColor = "bg-info"
+
+ if (IsAnimated == True):
+ ProgressAnim = "progress-bar-animated"
+ else:
+ ProgressAnim = ""
+
+ if (PMax > 0):
+ PWidth = int(round(int(PCur) / int(PMax) * 100))
+ else:
+ PWidth = 100
+ PCur = 100
+
+ HTML = """"""
+ if (MainDiv == True):
+ HTML += """
"""
+
+ return HTML
+
+
+################################################################################
+def Now(Format: int = 0):
+ """
+ Текущая дата и время:
+ * 0 - в секундах (timestamp, unixtime, отчет с 01.01.1970): int
+ * 1 - ГГГГММДД: str
+ * 2 - ГГГГ-ММ-ДД: str
+ * 10 - в миллисекундах (без точки, для уникальности файла или временной таблицы) (timestamp, unixtime, отчет с 01.01.1970): str
+ * 102 - ГГГГ-ММ-ДД ЧЧ:ММ:СС: str
+ """
+
+ if (Format == 0):
+ return int(str(time.mktime(datetime.datetime.now().timetuple())).split('.')[0])
+ if (Format == 10):
+ return str(str(time.mktime(datetime.datetime.now().timetuple())).replace(".", ""))
+ if (Format == 1):
+ return str(datetime.datetime.now().strftime("%Y%m%d"))
+ if (Format == 2):
+ return str(datetime.datetime.now().strftime("%Y-%m-%d"))
+ if (Format == 102):
+ return str(datetime.datetime.now()).split('.')[0]
+ return ""
+
+
+################################################################################
+def SaveLog(Filename, Value, ShowTimeInLog=True):
+ """Запись лога в конец"""
+
+ CurTime = datetime.datetime.now()
+ Today = CurTime.strftime("%Y-%m-%d")
+
+ # Проверяем и создаем путь для логов
+ if (os.path.exists("logs") == False):
+ os.mkdir("logs")
+ if (os.path.exists("logs") == False):
+ SaveLog(NameModule, "Не удается создать папку logs!")
+ return
+
+ file = open("logs/" + Filename + "_" + str(Today) + ".log", "a")
+ if (ShowTimeInLog == True):
+ file.write(str(CurTime) + ": " + Value + "\r\n")
+ else:
+ file.write(Value + "\r\n")
+ file.close()
+
+ if (app.config['SHOWLOG'] == True or GetVariable('Debug') == "1"):
+ print(str(CurTime) + ": " + Value)
+
+
+###############################################################################
+def validate(date_text: str):
+ """
+ Проверка что значение является датой.
+ На выходе datetime.datetime
+ :param date_text: Проверяемая дата
+ :return datetime.datetime
+ """
+ try:
+ # Проверка что есть доли секунды
+ if (date_text.find(".") < 0):
+ # Нет
+ return datetime.datetime.strptime(date_text, '%Y-%m-%d %H:%M:%S').strftime("%d.%m.%Y %H:%M:%S")
+ else:
+ # Есть
+ return datetime.datetime.strptime(date_text, '%Y-%m-%d %H:%M:%S.%f').strftime("%d.%m.%Y %H:%M:%S")
+ except Exception: # as E:
+ if (date_text == "None"):
+ return ""
+ else:
+ return date_text #
+
+
+###############################################################################
+def SQLEscape(Query: str) -> str:
+ """
+ Экранирование одинарных кавычек для исключения SQL-инъеккции в переменных
+ :param Query: часть запроса для экранирования
+ """
+ return Query.replace("'", "''")
+
+
+###############################################################################
+def ThreadVars_Message() -> str:
+ """Полоска для сообщений на всех страницах"""
+
+ return """{{ThreadVars.message}}
"""
+
+
+###############################################################################
+def UserHead(Vars):
+ """
+ Чтение заголовка из файла block_head для шаблона
+ :param Vars
+ """
+ CurStr = ""
+
+ if (os.path.exists("app/templates/block_head.htm") == True):
+ file = open("app/templates/block_head.htm", "r")
+ # SaveLog(NameModule, "Чтение заголовка")
+ CurStr = file.read()
+ file.close()
+
+ CurStr = CurStr.replace("{{ProgName}}", Vars['ProgName'])
+ CurStr = CurStr.replace("{{Title}}", Vars['Title'])
+ return CurStr
+
+
+###############################################################################
+def UserHeader(SessionID: str, Vars: dict) -> str:
+ """
+ Чтение заголовка из файла block_header для шаблона
+ :param SessionID: ID-сессии
+ :param Vars: Массив переменных
+ """
+ CurDataSession: DataSession = CheckSession(SessionID, False)
+ CurStr = ""
+
+ if (os.path.exists("app/templates/block_header.htm") == True):
+ file = open("app/templates/block_header.htm", "r")
+ # SaveLog(NameModule, "Чтение заголовка")
+ CurStr = file.read()
+ file.close()
+
+ CurStr = CurStr.replace("{{IPServer}}", request.host.split(":")[0])
+ if (request.args.get("Name") is None):
+ CurStr = CurStr.replace("{{PageName}}", request.url.split('/')[3])
+ else:
+ CurStr = CurStr.replace("{{PageName}}", request.url.split('/')[3].replace("?Name=", "/"))
+ CurStr = CurStr.replace("{{ProgName}}", Vars['ProgName'])
+ CurStr = CurStr.replace("{{Title}}", Vars['Title'])
+ CurStr = CurStr.replace("{{FIO}}", CurDataSession.username)
+
+ Vars.update({"CurStr": CurStr})
+ Vars.update({"request": request})
+
+ return API_App.UserHeader(SessionID, Vars)
+
+
+###############################################################################
+def UserErrorHeader(SessionID: str, Vars: dict) -> str:
+ """
+ Чтение заголовка из файла block_error_header для шаблона
+ :param SessionID: ID-сессии
+ :param Vars: Массив переменных
+ """
+ CurDataSession: DataSession = CheckSession(SessionID, False)
+ CurStr = ""
+
+ if (os.path.exists("app/templates/block_error_header.htm") == True):
+ file = open("app/templates/block_error_header.htm", "r")
+ # SaveLog(NameModule, "Чтение заголовка")
+ CurStr = file.read()
+ file.close()
+
+ CurStr = CurStr.replace("{{IPServer}}", request.host.split(":")[0])
+ if (request.args.get("Name") is None):
+ CurStr = CurStr.replace("{{PageName}}", request.url.split('/')[3])
+ else:
+ CurStr = CurStr.replace("{{PageName}}", request.url.split('/')[3].replace("?Name=", "/"))
+ CurStr = CurStr.replace("{{ProgName}}", Vars['ProgName'])
+ CurStr = CurStr.replace("{{Title}}", Vars['Title'])
+ CurStr = CurStr.replace("{{FIO}}", CurDataSession.username)
+ return CurStr
+
+
+###############################################################################
+def Modals(Vars) -> str:
+ """Чтение из файла block_modals для шаблона
+ :param Vars: Массив переменных. Пока не используется.
+ """
+ CurStr = ""
+
+ if (os.path.exists("app/templates/block_modals.htm") == True):
+ file = open("app/templates/block_modals.htm", "r")
+ # SaveLog(NameModule, "Чтение заголовка")
+ CurStr = file.read()
+ file.close()
+ return CurStr
+
+
+###############################################################################
+def UpdateSession(SessionID: str):
+ """
+ Процедура продляет существование сессии
+ """
+
+ if (app.config['SHMNAME'] == 'kmsik'):
+ return API_App.UpdateSession(SessionID) # Нестандартное обновление сессии
+
+ # Если сессия не существует или уже просрочена, то ничего не делаем
+ CurDataSession: DataSession = CheckSession(SessionID, False)
+ if (CurDataSession.status == False):
+ return
+
+ if (CurDataSession.sessionid != SessionID):
+ return
+
+ # Продляем проверочную сессию
+ DBEG = time.mktime(datetime.datetime.now().timetuple())
+ DEND = DBEG + int(app.config['MAX_TIME_SESSION']) # время сессии
+
+ # Продляем сессию еще на 15 минут
+ SaveDataSession(SessionID, "endsession", str(DEND))
+
+
+###############################################################################
+def ReadDataSession(SessionID, Variable, IsByte=False):
+ """
+ Считываем значение переменной сессии
+ :param SessionID: Сессия
+ :param Variable: Параметр
+ :param IsByte: Результат будет двоичные данные или нет
+ :return При нестандартной ситуации возвращает пустоту
+ """
+
+ TempVar = ""
+ # Проверяем и создаем путь для локальных сессий
+ if (os.path.exists("/dev/shm/" + app.config['SHMNAME'] + "") == False):
+ SaveLog(NameModule, "ReadDataSession: Путь не существует: /dev/shm/" + app.config['SHMNAME'] + "")
+ return ""
+ if (os.path.exists("/dev/shm/" + app.config['SHMNAME'] + "/sessions") == False):
+ SaveLog(NameModule, "ReadDataSession: Путь не существует: /dev/shm/" + app.config['SHMNAME'] + "/sessions")
+ return ""
+
+ # Проверяем наличие сессии
+ CurPath = "/dev/shm/" + app.config['SHMNAME'] + "/sessions/" + SessionID
+ if (os.path.exists(CurPath) == True):
+ if (os.path.exists(CurPath + "/" + Variable) == True):
+ if (IsByte == True):
+ file = open(CurPath + "/" + Variable, "rb")
+ else:
+ file = open(CurPath + "/" + Variable, "r")
+ TempVar = file.read()
+ file.close()
+ return TempVar
+ else:
+ SaveLog(NameModule, "ReadDataSession: Переменная не существует: " + CurPath + "/" + Variable)
+ else:
+ SaveLog(NameModule, "ReadDataSession: Путь не существует: " + CurPath)
+ return ""
+
+
+###############################################################################
+def SaveDataSession(SessionID, Variable, Value, IsByte=False) -> bool:
+ """
+ Сохраняем переменную сессии
+ :param SessionID: Сессия
+ :param Variable: Параметр
+ :param Value: Сохраняемое значение
+ :param IsByte: Сохранить бинарно или нет
+ :return При нестандартной ситуации возвращает False
+ """
+ # Проверяем и создаем путь для локальных сессий
+ if (os.path.exists("/dev/shm/" + app.config['SHMNAME'] + "") == False):
+ return False
+ if (os.path.exists("/dev/shm/" + app.config['SHMNAME'] + "/sessions") == False):
+ return False
+
+ # Проверяем наличие сессии
+ CurPath = "/dev/shm/" + app.config['SHMNAME'] + "/sessions/" + SessionID
+ if (os.path.exists(CurPath) == True):
+ if (IsByte == False):
+ file = open(CurPath + "/" + Variable, "w")
+ else:
+ file = open(CurPath + "/" + Variable, "wb")
+ file.write(Value)
+ file.close()
+ return True
+ return False
+
+
+###############################################################################
+def CheckSession(SessionID, FlagNeedUpdate) -> DataSession:
+ """
+ Процедура проверяет существование сессии
+ :param SessionID: Сессия
+ :param FlagNeedUpdate: Требуется ли обновление сессии при ее проверке
+ :return При существовании сессии .status = True
+ """
+ CurDataSession = DataSession()
+
+ # На пустую сессию ничего не возвращать
+ if (str(SessionID) == "" or SessionID is None):
+ CurDataSession.comment = "Не передан SessionID"
+ return CurDataSession
+
+ # Текущее время в секундах
+ DBEG = time.mktime(datetime.datetime.now().timetuple())
+ CurTime = datetime.datetime.now()
+
+ # Проверяем и создаем путь для локальных сессий
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}") == False):
+ os.mkdir(f"/dev/shm/{app.config['SHMNAME']}")
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}/sessions") == False):
+ CurDataSession.comment = "Не удается создать каталог для сессии"
+ return CurDataSession
+
+ # Проверяем наличие сессии
+ CurPath = f"/dev/shm/{app.config['SHMNAME']}/sessions/" + SessionID
+ if (os.path.exists(CurPath) == True):
+ EndSession = ReadDataSession(SessionID, "endsession")
+
+ # Если нет информации об окончании сессии пробуем еще раз
+ if (EndSession == ""):
+ time.sleep(1)
+ EndSession = ReadDataSession(SessionID, "endsession")
+
+ # Если так нет информации об окончании сессии то пишем ошибку
+ if (EndSession == ""):
+ SaveLog(NameModule, "CheckSession: Нет информации об окончании сессии: " + SessionID + ".")
+ CurDataSession.status = False
+ CurDataSession.comment = "Нет информации об окончании сессии"
+ return CurDataSession
+
+ # Если сессия уже истекла, то удаляем её
+ if (float(DBEG) > float(EndSession)):
+ SaveLog(NameModule, str(CurTime) + ": Сессия " + SessionID + " истекла.")
+ DeleteSession(SessionID)
+ CurDataSession.comment = "Сессия истекла"
+ return CurDataSession
+
+ # Если всё ОК то заполняем данные
+ CurDataSession.userid = str(ReadDataSession(SessionID, "userid"))
+ CurDataSession.username = str(ReadDataSession(SessionID, "username"))
+ CurDataSession.stationid = str(ReadDataSession(SessionID, "stationid"))
+ CurDataSession.sessionid = SessionID
+
+ # Продлеваем если необходимо
+ if (FlagNeedUpdate == True):
+ UpdateSession(SessionID)
+
+ CurDataSession.status = True
+ CurDataSession.comment = "Сессия существует"
+ return CurDataSession # возвращаем данные сессии
+
+ CurDataSession.comment = "Сессия не существует"
+ return CurDataSession # возвращаем данные сессии
+
+
+###############################################################################
+def CreateSession(UserID: str, UserName: str, StationID: str = "", SessionID: str = "", IPClient: str = ""):
+ """Процедура создает сессию, после успешного определения авторизации
+ :param UserID
+ :param UserName
+ :param StationID: Не обязательный параметр
+ :param SessionID: Не обязательный параметр
+ :param IPClient: Не обязательный параметр
+ """
+ if (app.config['SHMNAME'] == 'kmsik'):
+ return API_App.CreateSession(UserID, UserName, StationID, SessionID, IPClient) # Для КМС-ИК
+
+ # Удаление старых сессии
+ DeleteExpiredSession()
+
+ # защита от создания сессии на пустой ID
+ if (UserID == "" or UserID == "0"):
+ return ""
+
+ # Переменные
+ DBEG = time.mktime(datetime.datetime.now().timetuple())
+ DEND = DBEG + int(app.config['MAX_TIME_SESSION']) # время сессии
+ CurTime = datetime.datetime.now()
+ if (SessionID == ""):
+ SessionID = "S_" + str(uuid.uuid4())
+ SaveLog(NameModule, str(CurTime) + ": Создание сессии " + SessionID)
+
+ # Проверяем и создаем путь для локальных сессий
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}") == False):
+ os.mkdir(f"/dev/shm/{app.config['SHMNAME']}")
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}/sessions") == False):
+ os.mkdir(f"/dev/shm/{app.config['SHMNAME']}/sessions")
+
+ # создаем файл для сессии
+ CurPath = f"/dev/shm/{app.config['SHMNAME']}/sessions/" + SessionID
+ os.mkdir(CurPath)
+
+ # Сохраняем данные
+ SaveLog(NameModule, str(CurTime) + ": Сохраняем данные сессии " + SessionID)
+ SaveDataSession(SessionID, "ipclient", str(IPClient))
+ SaveDataSession(SessionID, "userid", str(UserID))
+ SaveDataSession(SessionID, "username", str(UserName))
+ SaveDataSession(SessionID, "startsession", str(DEND))
+ SaveDataSession(SessionID, "endsession", str(DEND))
+ SaveDataSession(SessionID, "stationid", str(StationID))
+
+ SaveLog(NameModule, "Успешный вход в систему: " + UserName)
+ return SessionID # возвращаем ID сессии
+
+
+###############################################################################
+def CreateSessionVK(UserID, UserName):
+ """
+ Процедура создает сессию, после успешного определения авторизации
+ :param UserID:
+ :param UserName:
+ """
+ # Удаление старых сессии
+ DeleteExpiredSession()
+
+ # защита от создания сессии на пустой ID
+ if (UserID == "" or UserID == "0"):
+ return ""
+
+ # Переменные
+ DBEG = time.mktime(datetime.datetime.now().timetuple())
+ DEND = DBEG + int(app.config['MAX_TIME_SESSION']) # время сессии
+ SessionID = str(uuid.uuid4())
+ CurTime = datetime.datetime.now()
+ SaveLog(NameModule, str(CurTime) + ": Создание сессии ВК " + SessionID)
+
+ # Проверяем и создаем путь для локальных сессий
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}") == False):
+ os.mkdir(f"/dev/shm/{app.config['SHMNAME']}")
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}/sessions") == False):
+ os.mkdir(f"/dev/shm/{app.config['SHMNAME']}/sessions")
+
+ # создаем файл для сессии
+ CurPath = f"/dev/shm/{app.config['SHMNAME']}/sessions/" + SessionID
+ os.mkdir(CurPath)
+
+ # Сохраняем данные
+ SaveLog(NameModule, str(CurTime) + ": Сохраняем данные сессии " + SessionID)
+ SaveDataSession(SessionID, "userid", str(UserID))
+ SaveDataSession(SessionID, "username", str(UserName))
+ SaveDataSession(SessionID, "startsession", str(DEND))
+ SaveDataSession(SessionID, "endsession", str(DEND))
+ SaveDataSession(SessionID, "fromvk", '1')
+
+ SaveLog(NameModule, "Успешный вход в систему из под ВК: " + UserName)
+
+ return SessionID # возвращаем ID сессии
+
+
+###############################################################################
+def DeleteExpiredSession():
+ """Процедура удаляет из базы истекшие сессии"""
+
+ # Текущее время в секундах
+ DBEG = time.mktime(datetime.datetime.now().timetuple())
+ CurTime = datetime.datetime.now()
+
+ SaveLog(NameModule, str(CurTime) + ": Запуск удаления старых сессии ")
+ # Проверяем и создаем путь для локальных сессий
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}") == False):
+ os.mkdir(f"/dev/shm/{app.config['SHMNAME']}")
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}/sessions") == False):
+ os.mkdir(f"/dev/shm/{app.config['SHMNAME']}/sessions")
+
+ # Получаем каталог сессии
+ for SessionID in os.listdir(f"/dev/shm/{app.config['SHMNAME']}/sessions"):
+ CurPath = f"/dev/shm/{app.config['SHMNAME']}/sessions/" + SessionID
+ # проверяем не файл ли это
+ if (os.path.isfile(CurPath) == False):
+ # проверяем срок сессии
+ EndSession = ReadDataSession(SessionID, "endsession")
+
+ if (EndSession == ""):
+ DeleteSession(SessionID)
+ else:
+ # Если сессия уже истекла, то удаляем её
+ if (float(DBEG) > float(EndSession)):
+ SaveLog(NameModule, str(CurTime) + ": Сессия " + SessionID + " истекла.")
+ DeleteSession(SessionID)
+
+
+###############################################################################
+def DeleteSession(SessionID):
+ """Процедура удаляет из базы конкретную сессию"""
+
+ CurTime = datetime.datetime.now()
+ if (SessionID == ""):
+ SaveLog(NameModule, str(CurTime) + ": Сессия для удаления не указана!")
+ return False
+
+ SaveLog(NameModule, str(CurTime) + ": Удаление сессии " + SessionID)
+
+ # Проверяем и создаем путь для локальных сессий
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}") == False):
+ os.mkdir(f"/dev/shm/{app.config['SHMNAME']}")
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}/sessions") == False):
+ os.mkdir(f"/dev/shm/{app.config['SHMNAME']}/sessions")
+
+ # создаем файл для сессии
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}/sessions/" + SessionID) == True):
+
+ # Удаляем все файлы сессии
+ for f in os.listdir(f"/dev/shm/{app.config['SHMNAME']}/sessions/" + SessionID):
+ os.remove(f"/dev/shm/{app.config['SHMNAME']}/sessions/{SessionID}/{f}")
+
+ # Удаляем папку сессии
+ os.rmdir(f"/dev/shm/{app.config['SHMNAME']}/sessions/" + SessionID)
+
+
+###############################################################################
+def GetVariable(Variable: str) -> str:
+ """
+ Считываем настройку программы
+ :param Variable: имя переменной
+ """
+
+ TempVar = ""
+
+ # Проверяем наличие сессии
+ CurPath = "db/Settings/"
+
+ # Если работает с Виртуальной памятью, то ищем настройки там
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}/Settings") == True):
+ CurPath = f"/dev/shm/{app.config['SHMNAME']}/Settings/"
+
+ if (os.path.exists(CurPath) == True):
+ if (os.path.exists(CurPath + "/" + Variable) == True):
+ file = open(CurPath + "/" + Variable, "r")
+ TempVar = file.read()
+ file.close()
+ return TempVar.strip()
+
+ return ""
+
+
+###############################################################################
+def SaveVariable(Variable, Value):
+ """
+ Сохраняем переменную от настроек программы
+ :param Variable: Параметр
+ :param Value: Сохраняемое значение
+ """
+
+ # Проверяем пусть
+ if (os.path.exists("db") == False):
+ return ""
+ if (os.path.exists("db/Settings") == False):
+ return ""
+
+ # Сохраняем настройки в программе
+ CurPath = "db/Settings/"
+ if (os.path.exists(CurPath) == True):
+ file = open(CurPath + "/" + Variable, "w")
+ file.write(str(Value))
+ file.close()
+
+ # Сохраняем настройки в памяти
+ CurPath = f"/dev/shm/{app.config['SHMNAME']}/Settings/"
+ if (os.path.exists(CurPath) == True):
+ file = open(CurPath + "/" + Variable, "w")
+ file.write(str(Value))
+ file.close()
+
+ return True
+ return False
+
+
+###############################################################################
+def GetSHA512_FromFile(Filename: str, secret: str) -> str:
+ """
+ Получить хэш на основе секретного ключа
+ :param Filename: путь к файлу
+ :param secret:
+ """
+ if (os.path.exists(Filename) == False):
+ return ""
+
+ # Расчет хэша
+ file = open(Filename, "rb")
+ CurStr = file.read()
+ file.close()
+ return GetSHA512_File(CurStr, secret)
+
+
+###############################################################################
+def GetSHA512_File(message: bytes, secret: str) -> str:
+ """
+ Получить хэш из массива байтов на основе секретного ключа
+ :param message: bytes
+ :param secret
+ """
+
+ h = hmac.new(bytearray(secret, "UTF-8"), message, hashlib.sha512)
+ return base64.b64encode(h.digest()).decode()
+
+
+###############################################################################
+def GetSHA512_Text(message: str, secret: str) -> str:
+ """Получить хэш из текста на основе секретного ключа"""
+ h = hmac.new(bytearray(secret, "UTF-8"), bytearray(message, "UTF-8"), hashlib.sha512)
+ return base64.b64encode(h.digest()).decode()
+
+
+###############################################################################
+def GetSHA1_Text(message: str) -> str:
+ """Получить хэш из текста на основе секретного ключа. Совместим с хэшем SQL 2005"""
+ hash_object = hashlib.sha1(bytearray(message, "UTF-8"))
+ hex_dig = hash_object.hexdigest()
+ return hex_dig
+
+
+###############################################################################
+def CheckPort(ip, port) -> str:
+ """
+ Проверка порта на его открытие
+ "OPEN" или "CLOSE"
+ """
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.settimeout(1)
+
+ ip = ip.split("\\")[0]
+ Attempt = 0
+ LastStatus = ""
+ SaveLog(NameModule, "Проверка порта на: " + ip + ":" + str(port))
+
+ # Проверяем трижды или до 1 успеха
+ while (Attempt < 3):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.settimeout(1)
+ try:
+ sock.connect((ip, int(port)))
+ SaveLog(NameModule, 'CheckPort: Порт ' + str(port) + ' открыт.')
+ # conn.close()
+ return "OPEN"
+ except Exception as E:
+ SaveLog(NameModule, "CheckPort: " + str(E))
+ # LastStatus = "ERROR"
+
+ time.sleep(1)
+ LastStatus = "CLOSE"
+ Attempt = Attempt + 1
+ SaveLog(NameModule, 'CheckPort: Порт ' + str(port) + ' закрыт.')
+ return LastStatus
+
+
+###############################################################################
+def RenameDataSession(SessionID, OldNameVar, NewNameVar) -> bool:
+ """
+ Переименовываем переменную сессии
+ """
+
+ # Проверяем и создаем путь для локальных сессий
+ if (os.path.exists("/dev/shm/" + app.config['SHMNAME'] + "") == False):
+ return False
+ if (os.path.exists("/dev/shm/" + app.config['SHMNAME'] + "/sessions") == False):
+ return False
+
+ # Проверяем наличие сессии
+ CurPath = "/dev/shm/" + app.config['SHMNAME'] + "/sessions/" + SessionID
+ if (os.path.exists(CurPath) == True):
+ os.rename(CurPath + "/" + OldNameVar, CurPath + "/" + NewNameVar)
+ return True
+ return False
+
+
+###############################################################################
+def RemoveFromFolder(folder, RemoveSelf=False) -> str:
+ """
+ Удалить всё из указанной папки
+ * RemoveSelf - Удалить саму папку (уже пустую)
+ return: Текст ошибки
+ """
+
+ print('Обработка папки на удаление : ' + folder)
+ for filename in os.listdir(folder): # Получаем список файлов в папке
+ file_path = os.path.join(folder, filename) # Полный путь к файлу или папке
+ try:
+ if os.path.isfile(file_path): # Если это файл
+ print('Удаляем файл: ' + file_path)
+ os.remove(file_path) # Удалить
+ elif os.path.isdir(file_path): # Если это папка
+ RemoveFromFolder(file_path) # Удаляем содержимое этой папки (рекурсия)
+ print('Удаляем папку : ' + file_path)
+ os.rmdir(file_path) # А затем удалить саму эту папку
+ except Exception as e:
+ TextLog = f'Не удалось удалить {file_path}. Причина: {e}'
+ SaveLog(NameModule, TextLog)
+ return TextLog
+ if (RemoveSelf == True):
+ print('Удаляем папку : ' + folder)
+ os.rmdir(folder) # А затем удалить саму эту папку
+ print('Обработка папки на удаление завершена: ' + folder)
+ return ""
+
+
+###############################################################################
+def RemoveDataSession(SessionID, Variable):
+ """
+ Удаляем переменную сессии
+ """
+ # Проверяем и создаем путь для локальных сессий
+ if (os.path.exists("/dev/shm/" + app.config['SHMNAME'] + "") == False):
+ return False
+ if (os.path.exists("/dev/shm/" + app.config['SHMNAME'] + "/sessions") == False):
+ return False
+
+ # Проверяем наличие сессии
+ CurPath = "/dev/shm/" + app.config['SHMNAME'] + "/sessions/" + SessionID
+ if (os.path.exists(CurPath) == True):
+ if (os.path.isfile(CurPath + "/" + Variable)):
+ os.remove(CurPath + "/" + Variable)
+ return True
+ return False
+
+
+########################################################################
+def GetSHA1_FromFile(Filename):
+ """Хэш для совместимости с MSSQL"""
+ if (os.path.exists(Filename) == False):
+ return ""
+
+ # Расчет хэша
+ file = open(Filename, "rb")
+ CurStr = file.read()
+ file.close()
+
+ hash_object = hashlib.sha1(CurStr)
+ hex_dig = hash_object.hexdigest()
+
+ return hex_dig
+
+
+########################################################################
+def ConvertStrToDate(date_text: str) -> datetime.datetime:
+ """
+ Проверка что строка является датой.
+ * Поддерживается ГГГГММДД, ГГГГ-ММ-ДД, ДД.ММ.ГГГГ.
+ * Поддерживается ГГГГ-ММ-ДД ЧЧ:ММ
+ * Поддерживается ГГГГ-ММ-ДД ЧЧ:ММ:СС
+ * Поддерживается ГГГГ-ММ-ДД ЧЧ:ММ:СС.мс
+ * Поддерживается с буквой T вместо пробела.
+
+
+ """
+ try:
+
+ date_text = date_text.strip()
+
+ # Если слишком мало текста для преобразования
+ # Дата не указана полностью
+ if (len(date_text) < 8 or len(date_text) == 9):
+ return None # type: ignore
+
+ # нет секунд. Вставляем их.
+ # print("date_text: " + date_text)
+ # print(f"len date_text: {len(date_text)}")
+ # print(f"""date_text.find("T": {date_text.find("T")}""")
+
+ if (len(date_text) == 16 and (date_text.find("T") == 10)):
+ date_text = date_text + ":00"
+ print("date_text: " + date_text)
+
+ # время не указано полностью
+ if (len(date_text) > 10 and len(date_text) < 19):
+ return None # type: ignore
+
+ # не 1-х и не 2-х тысячалетие
+ if (len(date_text) == 8 and date_text[:1] not in ('1', '2')):
+ return None # type: ignore
+
+ # ГГГГММДД
+ if (len(date_text) == 8):
+ return datetime.datetime.strptime(date_text, '%Y%m%d')
+
+ # Если точка идет 3-им символов (для формата ДД.ММ.ГГГГ )
+ if (date_text.find('.') == 2 or date_text.find('/') == 2 or date_text.find('-') == 2):
+ date_text = f"{date_text[6:10]}-{date_text[3:5]}-{date_text[0:2]}"
+
+ if (len(date_text) == 10):
+ return datetime.datetime.strptime(date_text.replace(".", "-"), '%Y-%m-%d')
+
+ # Если время разделено буквой T
+ if (date_text.find("T") > 0):
+ if (date_text.find(".") < 0): # Проверка что есть доли секунды
+ # Нет
+ return datetime.datetime.strptime(date_text, '%Y-%m-%dT%H:%M:%S')
+ else:
+ # Есть
+ return datetime.datetime.strptime(date_text, '%Y-%m-%dT%H:%M:%S.%f')
+ else:
+ # Проверка что есть доли секунды
+ if (date_text.find(".") < 0):
+ # Нет
+ return datetime.datetime.strptime(date_text, '%Y-%m-%d %H:%M:%S')
+ else:
+ # Есть
+ return datetime.datetime.strptime(date_text, '%Y-%m-%d %H:%M:%S.%f')
+ except Exception:
+ if (date_text == "None"):
+ return None # type: ignore
+ else:
+ return date_text # type: ignore
+
+
+###############################################################################
+def ConvertDate(d: datetime.datetime, NewType=102) -> str:
+ """
+ Преобразование даты в указанный формат:
+ * 102 - ГГГГ-ММ-ДД
+ * 104 - ДД.ММ.ГГГГ
+ * 107 - январь 2023
+ * 108 - Январь 2023
+ * 109 - ЯНВАРЬ 2023
+ * 112 - ГГГГММДД
+ * 122 - ГГГГ-ММ-ДД ЧЧ:ММ:СС
+
+ """
+ M = {}
+ M.update({"01": "январь"})
+ M.update({"02": "февраль"})
+ M.update({"03": "март"})
+ M.update({"04": "апрель"})
+ M.update({"05": "май"})
+ M.update({"06": "июнь"})
+ M.update({"07": "июль"})
+ M.update({"08": "август"})
+ M.update({"09": "сентябрь"})
+ M.update({"10": "октябрь"})
+ M.update({"11": "ноябрь"})
+ M.update({"12": "декабрь"})
+
+ if NewType == 102:
+ return d.strftime("%Y-%m-%d")
+ if NewType == 104:
+ return d.strftime("%d.%m.%Y")
+
+ if NewType == 107:
+ return M[d.strftime("%m")] + " " + d.strftime("%Y")
+ if NewType == 108:
+ return M[d.strftime("%m")].capitalize() + " " + d.strftime("%Y")
+ if NewType == 109:
+ return M[d.strftime("%m")].upper() + " " + d.strftime("%Y")
+
+ if NewType == 112:
+ return d.strftime("%Y%m%d")
+
+ if NewType == 122:
+ return d.strftime('%Y-%m-%d %H:%M:%S')
+
+ return ""
+
+
+###############################################################################
+def GetTZ():
+ """Получить текущий часовой пояс"""
+
+ now = datetime.datetime.now()
+ Z = str(now.replace(tzinfo=datetime.timezone.utc) - now.astimezone(datetime.timezone.utc)).split(':')[0]
+ return int(Z)
+
+
+###############################################################################
+def GetStatusService(ServiceName) -> str:
+ # Проверка статуса
+ Result = subprocess.run("systemctl -all | grep " + ServiceName, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
+
+ if (str(Result.returncode) == "1"):
+ return "None"
+
+ # Проверка статуса
+ p = subprocess.Popen(["systemctl", "is-active", ServiceName], stdout=subprocess.PIPE)
+ (output, err) = p.communicate()
+ output = output.decode('utf-8')
+ # print(output)
+ return output.strip()
+
+
+###############################################################################
+def CreateDBSettings():
+ """Создание базы и таблицы настроек"""
+
+ # Проверяем и создаем путь для локальных сессий
+ if (os.path.exists("db") == False):
+ os.mkdir("db")
+ if (os.path.exists("db/Settings") == False):
+ os.mkdir("db/Settings")
+
+
+###############################################################################
+def GetFromRequestsT(Url: str, Attempt=1, headers={}) -> str:
+ """Получить ответ по HTTP/S ссылке"""
+
+ if (headers == {}):
+ headers = {
+ 'Accept': '*/*',
+
+ 'Accept-Language': 'ru,en;q=0.9',
+ 'Connection': 'keep-alive',
+ 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.142 Safari/537.36',
+ }
+
+ # Сайты-исключения где не надо проверять сертификат.
+ CheckCert = True
+ # TODO: Узнать бы какой сертификат тут используется.
+ if (Url.find('opendata.digital.gov.ru') > 0):
+ CheckCert = False # Игнорировать или нет проверку сертификата для этого сайта
+
+ # При работе с локальными адресами используем свой сертификат
+ if (Url.find('192.168.') > 0 or Url.find('10.') > 0 or Url.find('127.0.0.1') > 0 or Url.find('rosminzdrav.ru') > 0):
+ CheckCert = "app/static/CA"
+
+ # Отправляем GET-запрос на URL
+ response = requests.get(Url, headers=headers, timeout=600, verify=CheckCert)
+
+ # Получаем статус-код ответа
+ status_code = response.status_code
+
+ if (str(status_code)[0] == "5"):
+ # Если получен код ошибки 502, повторяем запрос до 3 раз
+ if (Attempt <= 3):
+ return GetFromRequestsT(Url, Attempt + 1)
+ else:
+ return ""
+
+ # Возвращаем тело ответа в виде строки
+ return response.text
+
+
+###############################################################################
+def GetFromRequestsB(Url: str, Attempt=1, headers={}) -> bytes:
+ """
+ Получить ответ по HTTP/S ссылке.
+ Ответ в байтах. Например, скачаный двоичный файл.
+ """
+
+ if (headers == {}):
+ headers = {
+ 'Accept': '*/*',
+
+ 'Accept-Language': 'ru,en;q=0.9',
+ 'Connection': 'keep-alive',
+ 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.142 Safari/537.36',
+ }
+
+ # Сайты-исключения где не надо проверять сертификат.
+ CheckCert = True
+ # TODO: Узнать бы какой сертификат тут используется.
+ if (Url.find('opendata.digital.gov.ru') > 0):
+ CheckCert = False # Игнорировать или нет проверку сертификата для этого сайта
+
+ # При работе с локальными адресами используем свой сертификат
+ if (Url.find('192.168.') > 0 or Url.find('10.') > 0 or Url.find('127.0.0.1') > 0 or Url.find('rosminzdrav.ru') > 0):
+ CheckCert = "app/static/CA"
+
+ # Отправляем GET-запрос на URL
+ response = requests.get(Url, headers=headers, timeout=600, verify=CheckCert)
+
+ # Получаем статус-код ответа
+ status_code = response.status_code
+
+ if (str(status_code)[0] == "5"):
+ # Если получен код ошибки 502, повторяем запрос до 3 раз
+ if (Attempt <= 3):
+ return GetFromRequestsB(Url, Attempt + 1)
+ else:
+ return None # type: ignore
+
+ # Или в виде байтов
+ return response.content
+
+
+###############################################################################
+def PostFromRequestsB(Url: str, Data, Attempt=1, headers=None) -> bytes:
+ """
+ Получить ответ по HTTP/S ссылке.
+ Ответ в байтах. Например, скачаный двоичный файл.
+ """
+
+ if (headers is None):
+ headers = {
+ 'Accept': '*/*',
+
+ 'Accept-Language': 'ru,en;q=0.9',
+ 'Connection': 'keep-alive',
+ 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.142 Safari/537.36',
+ }
+
+ # Сайты-исключения где не надо проверять сертификат.
+ CheckCert = True
+ # TODO: Узнать бы какой сертификат тут используется.
+ if (Url.find('opendata.digital.gov.ru') > 0):
+ CheckCert = False # Игнорировать или нет проверку сертификата для этого сайта
+
+ # При работе с локальными адресами используем свой сертификат
+ if (Url.find('192.168.') > 0 or Url.find('10.') > 0 or Url.find('127.0.0.1') > 0 or Url.find('rosminzdrav.ru') > 0):
+ CheckCert = "app/static/CA"
+
+ # Отправляем POST-запрос на URL
+ response = requests.post(Url, headers=headers, timeout=600, verify=CheckCert, data=Data)
+
+ # Получаем статус-код ответа
+ status_code = response.status_code
+
+ if (str(status_code)[0] == "5"):
+ # Если получен код ошибки 502, повторяем запрос до 3 раз
+ if (Attempt <= 3):
+ return PostFromRequestsB(Url, Data, Attempt + 1)
+ else:
+ return None
+
+ # Или в виде байтов
+ return response.content
+
+
+###############################################################################
+def PostFromRequestsT(Url: str, data, Attempt=1, headers=None) -> dict:
+ """
+ Получить ответ по HTTP/S ссылке.
+ Ответ в виде текста.
+ """
+ Answer = {}
+ if (headers is None):
+ headers = {
+ 'Accept': '*/*',
+
+ 'Accept-Language': 'ru,en;q=0.9',
+ 'Connection': 'keep-alive',
+ 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.142 Safari/537.36',
+ }
+
+ # Сайты-исключения где не надо проверять сертификат.
+ CheckCert = True
+
+ # TODO: Узнать бы какой сертификат тут используется.
+ if (Url.find('opendata.digital.gov.ru') > 0):
+ CheckCert = False # Игнорировать или нет проверку сертификата для этого сайта
+
+ # При работе с локальными адресами используем свой сертификат
+ if (Url.find('192.168.') > 0 or Url.find('10.') > 0 or Url.find('127.0.0.1') > 0 or Url.find('rosminzdrav.ru') > 0):
+ CheckCert = "app/static/CA"
+
+ # Отправляем POST-запрос на URL
+ response = requests.post(Url, headers=headers, timeout=600, verify=CheckCert, data=data)
+
+ # Получаем статус-код ответа
+ status_code = response.status_code
+ Answer.update({"code": str(status_code)})
+ Answer.update({"text": response.text})
+
+ if (str(status_code)[0] == "5"):
+ # Если получен код ошибки 502, повторяем запрос до 3 раз
+ if (Attempt <= 3):
+ return PostFromRequestsT(Url, data, Attempt + 1)
+ else:
+ Answer.update({"status": "error"})
+ return Answer
+
+ if (str(status_code)[0] == "2"):
+ Answer.update({"status": "ok"})
+ else:
+ Answer.update({"status": "error"})
+
+ # Или в виде текста
+ return Answer
+
+
+###############################################################################
+def DayWeek(CurDate: datetime.datetime = datetime.datetime.today()):
+ "Получить день недели (по умолчанию - сегодня)"
+ weekday = CurDate.weekday()
+ return weekday + 1 # Что бы было от 1 до 7
+
+
+###############################################################################
+def SaveFile(Filename, Content, IsBinary=False) -> str:
+ """
+ Сохранить файл с заменой существующего файла.
+ * Filename - полный путь к файлу.
+ * Content - содержимое файла.
+ * IsBinary - является ли содержимое двоичным или нет.
+ return - пустота если всё хорошо или текст ошибки.
+ """
+ Mode = 'w' # Текстовый
+ if (IsBinary == True):
+ Mode = 'wb' # Бинарный
+
+ try:
+ with open(Filename, Mode) as fp:
+ fp.write(Content)
+ fp.close()
+ return '' # Всё хорошо
+ except Exception as E:
+ return str(E)
+
+
+###############################################################################
+def NewGUID() -> str:
+ "Сгененрировать новый GUID"
+ return str(uuid.uuid4())
diff --git a/app/source/OLD_API_Common.py b/app/source/OLD_API_Common.py
new file mode 100644
index 0000000..6a76950
--- /dev/null
+++ b/app/source/OLD_API_Common.py
@@ -0,0 +1,1084 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+API с общими функциями и процедурами
+Дата последней оптимизации: 22.05.2022
+Дата обновления: 11.01.2023
+"""
+
+import subprocess
+import uuid # Генератор GUID
+import datetime # Работа с датой
+import time # работа с временем
+import os # Работа с файловой системой
+import requests # Работа с http/s
+
+# Для Криптографии
+import hmac
+import base64
+import hashlib
+
+import socket # Для проверки доступности портов
+from app import app # чтение из config.py
+from flask import request # Для получения метаданных о запросе
+
+import sys # Подключаем свои библиотеки
+sys.path.append("app/source")
+import API_App
+
+appname = app.config['SHMNAME']
+NameModule = "API_Common"
+
+
+################################################################################
+class DataSession:
+ """Класс для передачи данных о сесссии"""
+
+ def __init__(self):
+ self.status: bool = False # Действует сессия или нет
+ self.comment: str = ""
+ self.sessionid: str = ""
+ self.userid: str = ""
+ self.username: str = ""
+ self.stationid: str = ""
+
+
+################################################################################
+def HTML_Progress(Color, IsAnimated: bool, PCur: int, PMax: int,) -> str:
+ """
+ Создание строки прогресса для HTML
+ :param Color: error, warning, blue, info, success
+ :param IsAnimated: True или False
+ :param PCur: Любое целое число
+ :param PMax: Любое целое число
+ :return: str
+ """
+ ProgressColor = ""
+
+ if (Color.lower() == "red" or Color.lower() == "danger" or Color.lower() == "error"):
+ ProgressColor = "bg-danger"
+
+ if (Color.lower() == "yellow" or Color.lower() == "warning"):
+ ProgressColor = "bg-warning"
+
+ if (Color.lower() == "green" or Color.lower() == "success"):
+ ProgressColor = "bg-success"
+
+ if (Color.lower() == "info"):
+ ProgressColor = "bg-info"
+
+ if (IsAnimated == True):
+ ProgressAnim = "progress-bar-animated"
+ else:
+ ProgressAnim = ""
+
+ if (PMax > 0):
+ PWidth = int(round(int(PCur) / int(PMax) * 100))
+ else:
+ PWidth = 100
+ PCur = 100
+
+ return f""""""
+
+
+################################################################################
+def Now(Format: int = 0):
+ """
+ Текущая дата и время:
+ * 0 - в секундах (timestamp, unixtime, отчет с 01.01.1970): int
+ * 1 - ГГГГММДД: str
+ * 2 - ГГГГ-ММ-ДД: str
+ * 10 - в миллисекундах (без точки, для уникальности файла или временной таблицы) (timestamp, unixtime, отчет с 01.01.1970): str
+ * 102 - ГГГГ-ММ-ДД ЧЧ:ММ:СС: str
+ """
+
+ if (Format == 0):
+ return int(str(time.mktime(datetime.datetime.now().timetuple())).split('.')[0])
+ if (Format == 10):
+ return str(str(time.mktime(datetime.datetime.now().timetuple())).replace(".", ""))
+ if (Format == 1):
+ return str(datetime.datetime.now().strftime("%Y%m%d"))
+ if (Format == 2):
+ return str(datetime.datetime.now().strftime("%Y-%m-%d"))
+ if (Format == 102):
+ return str(datetime.datetime.now()).split('.')[0]
+ return ""
+
+################################################################################
+
+
+def SaveLog(Filename, Value, ShowTimeInLog=True):
+ """Запись лога в конец"""
+
+ CurTime = datetime.datetime.now()
+ Today = CurTime.strftime("%Y-%m-%d")
+
+ # Проверяем и создаем путь для логов
+ if (os.path.exists("logs") == False):
+ os.mkdir("logs")
+ if (os.path.exists("logs") == False):
+ SaveLog(NameModule, "Не удается создать папку logs!")
+ return
+
+ file = open("logs/" + Filename + "_" + str(Today) + ".log", "a")
+ if (ShowTimeInLog == True):
+ file.write(str(CurTime) + ": " + Value + "\r\n")
+ else:
+ file.write(Value + "\r\n")
+ file.close()
+
+ if (app.config['SHOWLOG'] == True or GetVariable('Debug') == "1"):
+ print(str(CurTime) + ": " + Value)
+
+
+###############################################################################
+def validate(date_text: str):
+ """
+ Проверка что значение является датой.
+ На выходе datetime.datetime
+ :param date_text: Проверяемая дата
+ :return datetime.datetime
+ """
+ try:
+ # Проверка что есть доли секунды
+ if (date_text.find(".") < 0):
+ # Нет
+ return datetime.datetime.strptime(date_text, '%Y-%m-%d %H:%M:%S').strftime("%d.%m.%Y %H:%M:%S")
+ else:
+ # Есть
+ return datetime.datetime.strptime(date_text, '%Y-%m-%d %H:%M:%S.%f').strftime("%d.%m.%Y %H:%M:%S")
+ except Exception: # as E:
+ if (date_text == "None"):
+ return ""
+ else:
+ return date_text #
+
+
+###############################################################################
+def SQLEscape(Query: str) -> str:
+ """
+ Экранирование одинарных кавычек для исключения SQL-инъеккции в переменных
+ :param Query: часть запроса для экранирования
+ """
+ return Query.replace("'", "''")
+
+
+###############################################################################
+def ThreadVars_Message() -> str:
+ """Полоска для сообщений на всех страницах"""
+
+ return """{{ThreadVars.message}}
"""
+
+
+###############################################################################
+def UserHead(Vars):
+ """
+ Чтение заголовка из файла block_head для шаблона
+ :param Vars
+ """
+ CurStr = ""
+
+ if (os.path.exists("app/templates/block_head.htm") == True):
+ file = open("app/templates/block_head.htm", "r")
+ # SaveLog(NameModule, "Чтение заголовка")
+ CurStr = file.read()
+ file.close()
+
+ CurStr = CurStr.replace("{{ProgName}}", Vars['ProgName'])
+ CurStr = CurStr.replace("{{Title}}", Vars['Title'])
+ return CurStr
+
+
+###############################################################################
+def UserHeader(SessionID: str, Vars: dict) -> str:
+ """
+ Чтение заголовка из файла block_header для шаблона
+ :param SessionID: ID-сессии
+ :param Vars: Массив переменных
+ """
+ CurDataSession: DataSession = CheckSession(SessionID, False)
+ CurStr = ""
+
+ if (os.path.exists("app/templates/block_header.htm") == True):
+ file = open("app/templates/block_header.htm", "r")
+ # SaveLog(NameModule, "Чтение заголовка")
+ CurStr = file.read()
+ file.close()
+
+ CurStr = CurStr.replace("{{IPServer}}", request.host.split(":")[0])
+ if (request.args.get("Name") is None):
+ CurStr = CurStr.replace("{{PageName}}", request.url.split('/')[3])
+ else:
+ CurStr = CurStr.replace("{{PageName}}", request.url.split('/')[3].replace("?Name=", "/"))
+ CurStr = CurStr.replace("{{ProgName}}", Vars['ProgName'])
+ CurStr = CurStr.replace("{{Title}}", Vars['Title'])
+ CurStr = CurStr.replace("{{FIO}}", CurDataSession.username)
+
+ Vars.update({"CurStr": CurStr})
+ Vars.update({"request": request})
+
+ return API_App.UserHeader(SessionID, Vars)
+
+
+###############################################################################
+def UserErrorHeader(SessionID: str, Vars: dict) -> str:
+ """
+ Чтение заголовка из файла block_error_header для шаблона
+ :param SessionID: ID-сессии
+ :param Vars: Массив переменных
+ """
+ CurDataSession: DataSession = CheckSession(SessionID, False)
+ CurStr = ""
+
+ if (os.path.exists("app/templates/block_error_header.htm") == True):
+ file = open("app/templates/block_error_header.htm", "r")
+ # SaveLog(NameModule, "Чтение заголовка")
+ CurStr = file.read()
+ file.close()
+
+ CurStr = CurStr.replace("{{IPServer}}", request.host.split(":")[0])
+ if (request.args.get("Name") is None):
+ CurStr = CurStr.replace("{{PageName}}", request.url.split('/')[3])
+ else:
+ CurStr = CurStr.replace("{{PageName}}", request.url.split('/')[3].replace("?Name=", "/"))
+ CurStr = CurStr.replace("{{ProgName}}", Vars['ProgName'])
+ CurStr = CurStr.replace("{{Title}}", Vars['Title'])
+ CurStr = CurStr.replace("{{FIO}}", CurDataSession.username)
+ return CurStr
+
+
+###############################################################################
+def Modals(Vars) -> str:
+ """Чтение из файла block_modals для шаблона
+ :param Vars: Массив переменных. Пока не используется.
+ """
+ CurStr = ""
+
+ if (os.path.exists("app/templates/block_modals.htm") == True):
+ file = open("app/templates/block_modals.htm", "r")
+ # SaveLog(NameModule, "Чтение заголовка")
+ CurStr = file.read()
+ file.close()
+ return CurStr
+
+
+###############################################################################
+def UpdateSession(SessionID: str):
+ """
+ Процедура продляет существование сессии
+ """
+
+ if (app.config['SHMNAME'] == 'kmsik'): return API_App.UpdateSession(SessionID) # Нестандартное обновление сессии
+
+ # Если сессия не существует или уже просрочена, то ничего не делаем
+ CurDataSession: DataSession = CheckSession(SessionID, False)
+ if (CurDataSession.status == False): return
+
+ if (CurDataSession.sessionid != SessionID): return
+
+ # Продляем проверочную сессию
+ DBEG = time.mktime(datetime.datetime.now().timetuple())
+ DEND = DBEG + int(app.config['MAX_TIME_SESSION']) # время сессии
+
+ # Продляем сессию еще на 15 минут
+ SaveDataSession(SessionID, "endsession", str(DEND))
+
+
+###############################################################################
+def ReadDataSession(SessionID, Variable, IsByte=False):
+ """
+ Считываем значение переменной сессии
+ :param SessionID: Сессия
+ :param Variable: Параметр
+ :param IsByte: Результат будет двоичные данные или нет
+ :return При нестандартной ситуации возвращает пустоту
+ """
+
+ TempVar = ""
+ # Проверяем и создаем путь для локальных сессий
+ if (os.path.exists("/dev/shm/" + app.config['SHMNAME'] + "") == False):
+ SaveLog(NameModule, "ReadDataSession: Путь не существует: /dev/shm/" + app.config['SHMNAME'] + "")
+ return ""
+ if (os.path.exists("/dev/shm/" + app.config['SHMNAME'] + "/sessions") == False):
+ SaveLog(NameModule, "ReadDataSession: Путь не существует: /dev/shm/" + app.config['SHMNAME'] + "/sessions")
+ return ""
+
+ # Проверяем наличие сессии
+ CurPath = "/dev/shm/" + app.config['SHMNAME'] + "/sessions/" + SessionID
+ if (os.path.exists(CurPath) == True):
+ if (os.path.exists(CurPath + "/" + Variable) == True):
+ if (IsByte == True):
+ file = open(CurPath + "/" + Variable, "rb")
+ else:
+ file = open(CurPath + "/" + Variable, "r")
+ TempVar = file.read()
+ file.close()
+ return TempVar
+ else:
+ SaveLog(NameModule, "ReadDataSession: Переменная не существует: " + CurPath + "/" + Variable)
+ else:
+ SaveLog(NameModule, "ReadDataSession: Путь не существует: " + CurPath)
+ return ""
+
+
+###############################################################################
+def SaveDataSession(SessionID, Variable, Value, IsByte=False) -> bool:
+ """
+ Сохраняем переменную сессии
+ :param SessionID: Сессия
+ :param Variable: Параметр
+ :param Value: Сохраняемое значение
+ :param IsByte: Сохранить бинарно или нет
+ :return При нестандартной ситуации возвращает False
+ """
+ # Проверяем и создаем путь для локальных сессий
+ if (os.path.exists("/dev/shm/" + app.config['SHMNAME'] + "") == False):
+ return False
+ if (os.path.exists("/dev/shm/" + app.config['SHMNAME'] + "/sessions") == False):
+ return False
+
+ # Проверяем наличие сессии
+ CurPath = "/dev/shm/" + app.config['SHMNAME'] + "/sessions/" + SessionID
+ if (os.path.exists(CurPath) == True):
+ if (IsByte == False):
+ file = open(CurPath + "/" + Variable, "w")
+ else:
+ file = open(CurPath + "/" + Variable, "wb")
+ file.write(Value)
+ file.close()
+ return True
+ return False
+
+
+###############################################################################
+def CheckSession(SessionID, FlagNeedUpdate) -> DataSession:
+ """
+ Процедура проверяет существование сессии
+ :param SessionID: Сессия
+ :param FlagNeedUpdate: Требуется ли обновление сессии при ее проверке
+ :return При существовании сессии .status = True
+ """
+ CurDataSession = DataSession()
+
+ # На пустую сессию ничего не возвращать
+ if (str(SessionID) == "" or SessionID is None):
+ CurDataSession.comment = "Не передан SessionID"
+ return CurDataSession
+
+ # Текущее время в секундах
+ DBEG = time.mktime(datetime.datetime.now().timetuple())
+ CurTime = datetime.datetime.now()
+
+ # Проверяем и создаем путь для локальных сессий
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}") == False):
+ os.mkdir(f"/dev/shm/{app.config['SHMNAME']}")
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}/sessions") == False):
+ CurDataSession.comment = "Не удается создать каталог для сессии"
+ return CurDataSession
+
+ # Проверяем наличие сессии
+ CurPath = f"/dev/shm/{app.config['SHMNAME']}/sessions/" + SessionID
+ if (os.path.exists(CurPath) == True):
+ EndSession = ReadDataSession(SessionID, "endsession")
+
+ # Если нет информации об окончании сессии пробуем еще раз
+ if (EndSession == ""):
+ time.sleep(1)
+ EndSession = ReadDataSession(SessionID, "endsession")
+
+ # Если так нет информации об окончании сессии то пишем ошибку
+ if (EndSession == ""):
+ SaveLog(NameModule, "CheckSession: Нет информации об окончании сессии: " + SessionID + ".")
+ CurDataSession.status = False
+ CurDataSession.comment = "Нет информации об окончании сессии"
+ return CurDataSession
+
+ # Если сессия уже истекла, то удаляем её
+ if (float(DBEG) > float(EndSession)):
+ SaveLog(NameModule, str(CurTime) + ": Сессия " + SessionID + " истекла.")
+ DeleteSession(SessionID)
+ CurDataSession.comment = "Сессия истекла"
+ return CurDataSession
+
+ # Если всё ОК то заполняем данные
+ CurDataSession.userid = str(ReadDataSession(SessionID, "userid"))
+ CurDataSession.username = str(ReadDataSession(SessionID, "username"))
+ CurDataSession.stationid = str(ReadDataSession(SessionID, "stationid"))
+ CurDataSession.sessionid = SessionID
+
+ # Продлеваем если необходимо
+ if (FlagNeedUpdate == True): UpdateSession(SessionID)
+
+ CurDataSession.status = True
+ CurDataSession.comment = "Сессия существует"
+ return CurDataSession # возвращаем данные сессии
+
+ CurDataSession.comment = "Сессия не существует"
+ return CurDataSession # возвращаем данные сессии
+
+
+###############################################################################
+def CreateSession(UserID: str, UserName: str, StationID: str = "", SessionID: str = "", IPClient: str = ""):
+ """Процедура создает сессию, после успешного определения авторизации
+ :param UserID
+ :param UserName
+ :param StationID: Не обязательный параметр
+ :param SessionID: Не обязательный параметр
+ :param IPClient: Не обязательный параметр
+ """
+ if (app.config['SHMNAME'] == 'kmsik'): return API_App.CreateSession(UserID, UserName, StationID, SessionID, IPClient) # Для КМС-ИК
+
+ # Удаление старых сессии
+ DeleteExpiredSession()
+
+ # защита от создания сессии на пустой ID
+ if (UserID == "" or UserID == "0"):
+ return ""
+
+ # Переменные
+ DBEG = time.mktime(datetime.datetime.now().timetuple())
+ DEND = DBEG + int(app.config['MAX_TIME_SESSION']) # время сессии
+ CurTime = datetime.datetime.now()
+ if (SessionID == ""): SessionID = "S_" + str(uuid.uuid4())
+ SaveLog(NameModule, str(CurTime) + ": Создание сессии " + SessionID)
+
+ # Проверяем и создаем путь для локальных сессий
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}") == False):
+ os.mkdir(f"/dev/shm/{app.config['SHMNAME']}")
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}/sessions") == False):
+ os.mkdir(f"/dev/shm/{app.config['SHMNAME']}/sessions")
+
+ # создаем файл для сессии
+ CurPath = f"/dev/shm/{app.config['SHMNAME']}/sessions/" + SessionID
+ os.mkdir(CurPath)
+
+ # Сохраняем данные
+ SaveLog(NameModule, str(CurTime) + ": Сохраняем данные сессии " + SessionID)
+ SaveDataSession(SessionID, "ipclient", str(IPClient))
+ SaveDataSession(SessionID, "userid", str(UserID))
+ SaveDataSession(SessionID, "username", str(UserName))
+ SaveDataSession(SessionID, "startsession", str(DEND))
+ SaveDataSession(SessionID, "endsession", str(DEND))
+ SaveDataSession(SessionID, "stationid", str(StationID))
+
+ SaveLog(NameModule, "Успешный вход в систему: " + UserName)
+ return SessionID # возвращаем ID сессии
+
+
+###############################################################################
+def CreateSessionVK(UserID, UserName):
+ """
+ Процедура создает сессию, после успешного определения авторизации
+ :param UserID:
+ :param UserName:
+ """
+ # Удаление старых сессии
+ DeleteExpiredSession()
+
+ # защита от создания сессии на пустой ID
+ if (UserID == "" or UserID == "0"):
+ return ""
+
+ # Переменные
+ DBEG = time.mktime(datetime.datetime.now().timetuple())
+ DEND = DBEG + int(app.config['MAX_TIME_SESSION']) # время сессии
+ SessionID = str(uuid.uuid4())
+ CurTime = datetime.datetime.now()
+ SaveLog(NameModule, str(CurTime) + ": Создание сессии ВК " + SessionID)
+
+ # Проверяем и создаем путь для локальных сессий
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}") == False):
+ os.mkdir(f"/dev/shm/{app.config['SHMNAME']}")
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}/sessions") == False):
+ os.mkdir(f"/dev/shm/{app.config['SHMNAME']}/sessions")
+
+ # создаем файл для сессии
+ CurPath = f"/dev/shm/{app.config['SHMNAME']}/sessions/" + SessionID
+ os.mkdir(CurPath)
+
+ # Сохраняем данные
+ SaveLog(NameModule, str(CurTime) + ": Сохраняем данные сессии " + SessionID)
+ SaveDataSession(SessionID, "userid", str(UserID))
+ SaveDataSession(SessionID, "username", str(UserName))
+ SaveDataSession(SessionID, "startsession", str(DEND))
+ SaveDataSession(SessionID, "endsession", str(DEND))
+ SaveDataSession(SessionID, "fromvk", '1')
+
+ SaveLog(NameModule, "Успешный вход в систему из под ВК: " + UserName)
+
+ return SessionID # возвращаем ID сессии
+
+
+###############################################################################
+def DeleteExpiredSession():
+ """Процедура удаляет из базы истекшие сессии"""
+
+ # Текущее время в секундах
+ DBEG = time.mktime(datetime.datetime.now().timetuple())
+ CurTime = datetime.datetime.now()
+
+ SaveLog(NameModule, str(CurTime) + ": Запуск удаления старых сессии ")
+ # Проверяем и создаем путь для локальных сессий
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}") == False):
+ os.mkdir(f"/dev/shm/{app.config['SHMNAME']}")
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}/sessions") == False):
+ os.mkdir(f"/dev/shm/{app.config['SHMNAME']}/sessions")
+
+ # Получаем каталог сессии
+ for SessionID in os.listdir(f"/dev/shm/{app.config['SHMNAME']}/sessions"):
+ CurPath = f"/dev/shm/{app.config['SHMNAME']}/sessions/" + SessionID
+ # проверяем не файл ли это
+ if (os.path.isfile(CurPath) == False):
+ # проверяем срок сессии
+ EndSession = ReadDataSession(SessionID, "endsession")
+
+ if (EndSession == ""):
+ DeleteSession(SessionID)
+ else:
+ # Если сессия уже истекла, то удаляем её
+ if (float(DBEG) > float(EndSession)):
+ SaveLog(NameModule, str(CurTime) + ": Сессия " + SessionID + " истекла.")
+ DeleteSession(SessionID)
+
+
+###############################################################################
+def DeleteSession(SessionID):
+ """Процедура удаляет из базы конкретную сессию"""
+
+ CurTime = datetime.datetime.now()
+ if (SessionID == ""):
+ SaveLog(NameModule, str(CurTime) + ": Сессия для удаления не указана!")
+ return False
+
+ SaveLog(NameModule, str(CurTime) + ": Удаление сессии " + SessionID)
+
+ # Проверяем и создаем путь для локальных сессий
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}") == False):
+ os.mkdir(f"/dev/shm/{app.config['SHMNAME']}")
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}/sessions") == False):
+ os.mkdir(f"/dev/shm/{app.config['SHMNAME']}/sessions")
+
+ # создаем файл для сессии
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}/sessions/" + SessionID) == True):
+
+ # Удаляем все файлы сессии
+ for f in os.listdir(f"/dev/shm/{app.config['SHMNAME']}/sessions/" + SessionID):
+ os.remove(f"/dev/shm/{app.config['SHMNAME']}/sessions/{SessionID}/{f}")
+
+ # Удаляем папку сессии
+ os.rmdir(f"/dev/shm/{app.config['SHMNAME']}/sessions/" + SessionID)
+
+
+###############################################################################
+def GetVariable(Variable: str) -> str:
+ """
+ Считываем настройку программы
+ :param Variable: имя переменной
+ """
+
+ TempVar = ""
+
+ # Проверяем наличие сессии
+ CurPath = "db/Settings/"
+
+ # Если работает с Виртуальной памятью, то ищем настройки там
+ if (os.path.exists(f"/dev/shm/{app.config['SHMNAME']}/Settings") == True):
+ CurPath = f"/dev/shm/{app.config['SHMNAME']}/Settings/"
+
+ if (os.path.exists(CurPath) == True):
+ if (os.path.exists(CurPath + "/" + Variable) == True):
+ file = open(CurPath + "/" + Variable, "r")
+ TempVar = file.read()
+ file.close()
+ return TempVar.strip()
+
+ return ""
+
+
+###############################################################################
+def SaveVariable(Variable, Value):
+ """
+ Сохраняем переменную от настроек программы
+ :param Variable: Параметр
+ :param Value: Сохраняемое значение
+ """
+
+ # Проверяем пусть
+ if (os.path.exists("db") == False):
+ return ""
+ if (os.path.exists("db/Settings") == False):
+ return ""
+
+ # Сохраняем настройки в программе
+ CurPath = "db/Settings/"
+ if (os.path.exists(CurPath) == True):
+ file = open(CurPath + "/" + Variable, "w")
+ file.write(str(Value))
+ file.close()
+
+ # Сохраняем настройки в памяти
+ CurPath = f"/dev/shm/{app.config['SHMNAME']}/Settings/"
+ if (os.path.exists(CurPath) == True):
+ file = open(CurPath + "/" + Variable, "w")
+ file.write(str(Value))
+ file.close()
+
+ return True
+ return False
+
+
+###############################################################################
+def GetSHA512_FromFile(Filename: str, secret: str) -> str:
+ """
+ Получить хэш на основе секретного ключа
+ :param Filename: путь к файлу
+ :param secret:
+ """
+ if (os.path.exists(Filename) == False): return ""
+
+ # Расчет хэша
+ file = open(Filename, "rb")
+ CurStr = file.read()
+ file.close()
+ return GetSHA512_File(CurStr, secret)
+
+
+###############################################################################
+def GetSHA512_File(message: bytes, secret: str) -> str:
+ """
+ Получить хэш из массива байтов на основе секретного ключа
+ :param message: bytes
+ :param secret
+ """
+
+ h = hmac.new(bytearray(secret, "UTF-8"), message, hashlib.sha512)
+ return base64.b64encode(h.digest()).decode()
+
+
+###############################################################################
+def GetSHA512_Text(message: str, secret: str) -> str:
+ """Получить хэш из текста на основе секретного ключа"""
+ h = hmac.new(bytearray(secret, "UTF-8"), bytearray(message, "UTF-8"), hashlib.sha512)
+ return base64.b64encode(h.digest()).decode()
+
+
+###############################################################################
+def GetSHA1_Text(message: str) -> str:
+ """Получить хэш из текста на основе секретного ключа. Совместим с хэшем SQL 2005"""
+ hash_object = hashlib.sha1(bytearray(message, "UTF-8"))
+ hex_dig = hash_object.hexdigest()
+ return hex_dig
+
+
+###############################################################################
+def CheckPort(ip, port) -> str:
+ """
+ Проверка порта на его открытие
+ "OPEN" или "CLOSE"
+ """
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.settimeout(1)
+
+ ip = ip.split("\\")[0]
+ Attempt = 0
+ LastStatus = ""
+ SaveLog(NameModule, "Проверка порта на: " + ip + ":" + str(port))
+
+ # Проверяем трижды или до 1 успеха
+ while (Attempt < 3):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.settimeout(1)
+ try:
+ sock.connect((ip, int(port)))
+ SaveLog(NameModule, 'CheckPort: Порт ' + str(port) + ' открыт.')
+ # conn.close()
+ return "OPEN"
+ except Exception as E:
+ SaveLog(NameModule, "CheckPort: " + str(E))
+ # LastStatus = "ERROR"
+
+ time.sleep(1)
+ LastStatus = "CLOSE"
+ Attempt = Attempt + 1
+ SaveLog(NameModule, 'CheckPort: Порт ' + str(port) + ' закрыт.')
+ return LastStatus
+
+
+###############################################################################
+def RenameDataSession(SessionID, OldNameVar, NewNameVar) -> bool:
+ """
+ Переименовываем переменную сессии
+ """
+
+ # Проверяем и создаем путь для локальных сессий
+ if (os.path.exists("/dev/shm/" + app.config['SHMNAME'] + "") == False):
+ return False
+ if (os.path.exists("/dev/shm/" + app.config['SHMNAME'] + "/sessions") == False):
+ return False
+
+ # Проверяем наличие сессии
+ CurPath = "/dev/shm/" + app.config['SHMNAME'] + "/sessions/" + SessionID
+ if (os.path.exists(CurPath) == True):
+ os.rename(CurPath + "/" + OldNameVar, CurPath + "/" + NewNameVar)
+ return True
+ return False
+
+
+###############################################################################
+def RemoveFromFolder(folder, RemoveSelf=False) -> str:
+ """
+ Удалить всё из указанной папки
+ * RemoveSelf - Удалить саму папку (уже пустую)
+ return: Текст ошибки
+ """
+
+ print('Обработка папки на удаление : ' + folder)
+ for filename in os.listdir(folder): # Получаем список файлов в папке
+ file_path = os.path.join(folder, filename) # Полный путь к файлу или папке
+ try:
+ if os.path.isfile(file_path): # Если это файл
+ print('Удаляем файл: ' + file_path)
+ os.remove(file_path) # Удалить
+ elif os.path.isdir(file_path): # Если это папка
+ RemoveFromFolder(file_path) # Удаляем содержимое этой папки (рекурсия)
+ print('Удаляем папку : ' + file_path)
+ os.rmdir(file_path) # А затем удалить саму эту папку
+ except Exception as e:
+ TextLog = f'Не удалось удалить {file_path}. Причина: {e}'
+ SaveLog(NameModule, TextLog)
+ return TextLog
+ if (RemoveSelf == True):
+ print('Удаляем папку : ' + folder)
+ os.rmdir(folder) # А затем удалить саму эту папку
+ print('Обработка папки на удаление завершена: ' + folder)
+ return ""
+
+
+###############################################################################
+def RemoveDataSession(SessionID, Variable):
+ """
+ Удаляем переменную сессии
+ """
+ # Проверяем и создаем путь для локальных сессий
+ if (os.path.exists("/dev/shm/" + app.config['SHMNAME'] + "") == False):
+ return False
+ if (os.path.exists("/dev/shm/" + app.config['SHMNAME'] + "/sessions") == False):
+ return False
+
+ # Проверяем наличие сессии
+ CurPath = "/dev/shm/" + app.config['SHMNAME'] + "/sessions/" + SessionID
+ if (os.path.exists(CurPath) == True):
+ if (os.path.isfile(CurPath + "/" + Variable)): os.remove(CurPath + "/" + Variable)
+ return True
+ return False
+
+
+########################################################################
+def GetSHA1_FromFile(Filename):
+ """Хэш для совместимости с MSSQL"""
+ if (os.path.exists(Filename) == False): return ""
+
+ # Расчет хэша
+ file = open(Filename, "rb")
+ CurStr = file.read()
+ file.close()
+
+ hash_object = hashlib.sha1(CurStr)
+ hex_dig = hash_object.hexdigest()
+
+ return hex_dig
+
+
+########################################################################
+def ConvertStrToDate(date_text: str) -> datetime.datetime:
+ """
+ Проверка что строка является датой.
+ Поддерживается ГГГГММДД, ГГГГ-ММ-ДД, ДД.ММ.ГГГГ.
+ """
+ try:
+
+ date_text = date_text.strip()
+ # Если слишком мало текста для преобразования
+ # Дата не указана полностью
+ if (len(date_text) < 8 or len(date_text) == 9): return None # type: ignore
+
+ # время не указано полностью
+ if (len(date_text) > 10 and len(date_text) < 19): return None # type: ignore
+
+ # не 1-х и не 2-х тысячалетие
+ if (len(date_text) == 8 and date_text[:1] not in ('1', '2')): return None # type: ignore
+
+ # ГГГГММДД
+ if (len(date_text) == 8):
+ return datetime.datetime.strptime(date_text, '%Y%m%d')
+
+ # Если точка идет 3-им символов (для формата ДД.ММ.ГГГГ )
+ if (date_text.find('.') == 2 or date_text.find('/') == 2 or date_text.find('-') == 2):
+ date_text = f"{date_text[6:10]}-{date_text[3:5]}-{date_text[0:2]}"
+
+ if (len(date_text) == 10):
+ return datetime.datetime.strptime(date_text.replace(".", "-"), '%Y-%m-%d')
+
+ # Проверка что есть доли секунды
+ if (date_text.find(".") < 0):
+ # Нет
+ return datetime.datetime.strptime(date_text, '%Y-%m-%d %H:%M:%S')
+ else:
+ # Есть
+ return datetime.datetime.strptime(date_text, '%Y-%m-%d %H:%M:%S.%f')
+ except Exception:
+ if (date_text == "None"):
+ return None # type: ignore
+ else:
+ return date_text # type: ignore
+
+
+###############################################################################
+def ConvertDate(d: datetime.datetime, NewType=102) -> str:
+ """
+ Преобразование даты в указанный формат
+ 102 - ГГГГ-ММ-ДД
+ 104 - ДД.ММ.ГГГГ
+ 107 - январь 2023
+ 108 - Январь 2023
+ 109 - ЯНВАРЬ 2023
+ """
+ M = {}
+ M.update({"01": "январь"})
+ M.update({"02": "февраль"})
+ M.update({"03": "март"})
+ M.update({"04": "апрель"})
+ M.update({"05": "май"})
+ M.update({"06": "июнь"})
+ M.update({"07": "июль"})
+ M.update({"08": "август"})
+ M.update({"09": "сентябрь"})
+ M.update({"10": "октябрь"})
+ M.update({"11": "ноябрь"})
+ M.update({"12": "декабрь"})
+
+ if NewType == 112:
+ return d.strftime("%Y%m%d")
+ if NewType == 102:
+ return d.strftime("%Y-%m-%d")
+ if NewType == 104:
+ return d.strftime("%d.%m.%Y")
+
+ if NewType == 107:
+ return M[d.strftime("%m")] + " " + d.strftime("%Y")
+ if NewType == 108:
+ return M[d.strftime("%m")].capitalize() + " " + d.strftime("%Y")
+ if NewType == 109:
+ return M[d.strftime("%m")].upper() + " " + d.strftime("%Y")
+ return ""
+
+
+###############################################################################
+def GetTZ():
+ """Получить текущий часовой пояс"""
+
+ now = datetime.datetime.now()
+ Z = str(now.replace(tzinfo=datetime.timezone.utc) - now.astimezone(datetime.timezone.utc)).split(':')[0]
+ return int(Z)
+
+
+###############################################################################
+def GetStatusService(ServiceName) -> str:
+ # Проверка статуса
+ Result = subprocess.run("systemctl -all | grep " + ServiceName, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
+
+ if (str(Result.returncode) == "1"): return "None"
+
+ # Проверка статуса
+ p = subprocess.Popen(["systemctl", "is-active", ServiceName], stdout=subprocess.PIPE)
+ (output, err) = p.communicate()
+ output = output.decode('utf-8')
+ # print(output)
+ return output.strip()
+
+
+###############################################################################
+def CreateDBSettings():
+ """Создание базы и таблицы настроек"""
+
+ # Проверяем и создаем путь для локальных сессий
+ if (os.path.exists("db") == False):
+ os.mkdir("db")
+ if (os.path.exists("db/Settings") == False):
+ os.mkdir("db/Settings")
+
+
+###############################################################################
+def GetFromRequestsT(Url: str, Attempt=1, headers={}) -> str:
+ """Получить ответ по HTTP/S ссылке"""
+
+ if (headers == {}):
+ headers = {
+ 'Accept': '*/*',
+
+ 'Accept-Language': 'ru,en;q=0.9',
+ 'Connection': 'keep-alive',
+ 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.142 Safari/537.36',
+ }
+
+ # Сайты-исключения где не надо проверять сертификат.
+ CheckCert = True
+ if (Url.find('opendata.digital.gov.ru') > 0):
+ CheckCert = False # Игнорировать или нет проверку сертификата для этого сайта
+
+ # При работе с локальными адресами используем свой сертификат
+ if (Url.find('192.168.') > 0 or Url.find('10.') > 0 or Url.find('127.0.0.1') > 0):
+ CheckCert = "app/static/CA"
+
+ # Отправляем GET-запрос на URL
+ response = requests.get(Url, headers=headers, timeout=600, verify=CheckCert)
+
+ # Получаем статус-код ответа
+ status_code = response.status_code
+
+ if (str(status_code)[0] == "5"):
+ # Если получен код ошибки 502, повторяем запрос до 3 раз
+ if (Attempt <= 3):
+ return GetFromRequestsT(Url, Attempt + 1)
+ else:
+ return ""
+
+ # Возвращаем тело ответа в виде строки
+ return response.text
+
+
+###############################################################################
+def GetFromRequestsB(Url: str, Attempt=1, headers={}) -> bytes:
+ """
+ Получить ответ по HTTP/S ссылке.
+ Ответ в байтах. Например, скачаный двоичный файл.
+ """
+
+ if (headers == {}):
+ headers = {
+ 'Accept': '*/*',
+
+ 'Accept-Language': 'ru,en;q=0.9',
+ 'Connection': 'keep-alive',
+ 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.142 Safari/537.36',
+ }
+
+ # Сайты-исключения где не надо проверять сертификат.
+ CheckCert = True
+ if (Url.find('opendata.digital.gov.ru') > 0):
+ CheckCert = False # Игнорировать или нет проверку сертификата для этого сайта
+
+ # При работе с локальными адресами используем свой сертификат
+ if (Url.find('192.168.') > 0 or Url.find('10.') > 0 or Url.find('127.0.0.1') > 0):
+ CheckCert = "app/static/CA"
+
+ # Отправляем GET-запрос на URL
+ response = requests.get(Url, headers=headers, timeout=600, verify=CheckCert)
+
+ # Получаем статус-код ответа
+ status_code = response.status_code
+
+ if (str(status_code)[0] == "5"):
+ # Если получен код ошибки 502, повторяем запрос до 3 раз
+ if (Attempt <= 3):
+ return GetFromRequestsB(Url, Attempt + 1)
+ else:
+ return None
+
+ # Или в виде байтов
+ return response.content
+
+
+###############################################################################
+def PostFromRequestsB(Url: str, Data, Attempt=1, headers={}) -> bytes:
+ """
+ Получить ответ по HTTP/S ссылке.
+ Ответ в байтах. Например, скачаный двоичный файл.
+ """
+
+ if (headers == {}):
+ headers = {
+ 'Accept': '*/*',
+
+ 'Accept-Language': 'ru,en;q=0.9',
+ 'Connection': 'keep-alive',
+ 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.142 Safari/537.36',
+ }
+
+ # Сайты-исключения где не надо проверять сертификат.
+ CheckCert = True
+
+ if (Url.find('opendata.digital.gov.ru') > 0):
+ CheckCert = False # Игнорировать или нет проверку сертификата для этого сайта
+
+ # При работе с локальными адресами используем свой сертификат
+ if (Url.find('192.168.') > 0 or Url.find('10.') > 0 or Url.find('127.0.0.1') > 0):
+ CheckCert = "app/static/CA"
+
+ # Отправляем POST-запрос на URL
+ response = requests.post(Url, headers=headers, timeout=600, verify=CheckCert, data=Data)
+
+ # Получаем статус-код ответа
+ status_code = response.status_code
+
+ if (str(status_code)[0] == "5"):
+ # Если получен код ошибки 502, повторяем запрос до 3 раз
+ if (Attempt <= 3):
+ return PostFromRequestsB(Url, Data, Attempt + 1)
+ else:
+ return None
+
+ # Или в виде байтов
+ return response.content
+
+
+###############################################################################
+def PostFromRequestsT(Url: str, data, Attempt=1, headers={}) -> str:
+ """
+ Получить ответ по HTTP/S ссылке.
+ Ответ в виде текста.
+ """
+ if (headers == {}):
+ headers = {
+ 'Accept': '*/*',
+
+ 'Accept-Language': 'ru,en;q=0.9',
+ 'Connection': 'keep-alive',
+ 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.142 Safari/537.36',
+ }
+
+ # Сайты-исключения где не надо проверять сертификат.
+ CheckCert = True
+
+ if (Url.find('opendata.digital.gov.ru') > 0):
+ CheckCert = False # Игнорировать или нет проверку сертификата для этого сайта
+
+ # При работе с локальными адресами используем свой сертификат
+ if (Url.find('192.168.') > 0 or Url.find('10.') > 0 or Url.find('127.0.0.1') > 0):
+ CheckCert = "app/static/CA"
+
+ # Отправляем POST-запрос на URL
+ response = requests.post(Url, headers=headers, timeout=600, verify=CheckCert, data=data)
+
+ # Получаем статус-код ответа
+ status_code = response.status_code
+
+ if (str(status_code)[0] == "5"):
+ # Если получен код ошибки 502, повторяем запрос до 3 раз
+ if (Attempt <= 3):
+ return PostFromRequestsT(Url, data, Attempt + 1)
+ else:
+ return ""
+
+ # Или в виде текста
+ return response.text
+
+
+###############################################################################
+def DayWeek(CurDate: datetime.datetime = datetime.datetime.today()):
+ "Получить день недели (по умолчанию - сегодня)"
+ weekday = CurDate.weekday()
+ return weekday + 1 # Что бы было от 1 до 7
diff --git a/app/source/OLD_Pages.py b/app/source/OLD_Pages.py
new file mode 100644
index 0000000..c7f1fb2
--- /dev/null
+++ b/app/source/OLD_Pages.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Дата оптимизации: 09.06.2022
+Дата тестирования: 14.06.2022
+"""
+import os
+
+# Активация FLASK
+from app import app # чтение из config.py
+from flask import render_template # использование шаблонов
+# from flask import request # получение данных Cookie, GET и POST
+from flask import jsonify # ответ в формате JSON
+# from flask import redirect # Код страницы и перенаравление
+# import json
+import sys
+import configparser
+import importlib.util
+
+# Подключаем свои библиотеки
+sys.path.append("app/source")
+import API_Common as APIC
+import API_App
+# import API_MSSQL
+# import API_App
+
+
+###############################################################################
+# Проверка сессии
+###############################################################################
+def CheckSession(SessionID):
+ if SessionID is not None:
+ if (SessionID != ""):
+ CurDataSession: APIC.DataSession = APIC.CheckSession(SessionID, True)
+ if (CurDataSession.status == False): return False
+
+ CurSessionID = CurDataSession.sessionid
+
+ # Если нет в базе такой сессии, то заного авторизуемся
+ if (CurSessionID == ""): return False
+
+ return True
+ else:
+ return False
+ else:
+ return False
+
+
+###############################################################################
+# POST
+###############################################################################
+def API(SessionID, CurPage):
+ config = configparser.ConfigParser()
+ config.read("app/pages/" + CurPage + "/config.ini")
+
+ VarArray = {}
+ if (CheckSession(SessionID) == False) and config["ACCESS"]["NeedAuth"] == 1:
+ return "Требуется повторная авторизация. Нажмите F5.", 401
+
+ if (CurPage != ""):
+ spec = importlib.util.spec_from_file_location("module.name", f"app/pages/{CurPage}/source.py")
+ CurPage = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(CurPage)
+ return CurPage.API(SessionID)
+
+ VarArray.update({'status': 'Неизвестная операция'})
+ return jsonify(VarArray)
+
+
+###############################################################################
+# MAIN
+###############################################################################
+def Main(SessionID, CurPage):
+ # Объявление переменных
+ Var = {"Title": "", "ProgName": app.config['PROGNAME']}
+ Modal_Vars = {}
+
+ S = APIC.CheckSession(SessionID, False)
+
+ # Получаем уровень доступа
+ if (S.userid not in ("-1", "")):
+ UserData: API_MSSQL.Result = API_App.GetDataUserByID(APIC.ReadDataSession(SessionID, "userid"))
+
+ # Создаем страничку
+ try:
+ if (CurPage is None):
+ return render_template('Pages.htm', head=APIC.UserHead(Var) # Подключаем раздел head со скриптами и css
+ , UserHeader=APIC.UserHeader(SessionID, Var) # Рисуем шапку на странице
+ , Modals=APIC.Modals(Modal_Vars) # Модальные окна
+ , ThreadVars_message=APIC.ThreadVars_Message() #
+ , version=app.config['VERSION'] #
+ , R_category="{{R.category}}" #
+ , R_name="{{R.name}}" #
+ , R_description="{{R.description}}" #
+ , R_autor="{{R.autor}}" #
+ , R_tags="{{R.tags}}" #
+ , CurTag="{{CurTag}}" #
+ )
+
+ # Если вошли в режиме Установщика то проверку не проверяем
+ if (S.userid != "-1"):
+ # Проверка прав на модуль
+ if (os.path.isfile("app/pages/" + CurPage + "/config.ini") == False):
+ return render_template('error_403.htm', head=APIC.UserHead(Var) # Подключаем раздел head со скриптами и css
+ , UserHeader=APIC.UserErrorHeader(SessionID, Var) # Рисуем шапку на странице
+ , ThreadVars_message=f"Не удалось получить настройки для страницы {CurPage}. Похоже, эта страница на стадии разработки")
+
+ config = configparser.ConfigParser()
+ config.read("app/pages/" + CurPage + "/config.ini")
+
+ if (config["ACCESS"]["UserInterFace"] not in ("0", "")):
+ if (
+ str(UserData.GetRecord(0, "UserInterFace")) not in config["ACCESS"]["UserInterFace"].split(',')
+ # or AccessOMS < int(config["ACCESS"]["AccessOMS"])
+ or int(UserData.GetRecord(0, "AccessOMS")) < int(config["ACCESS"]["AccessOMS"])
+ or int(UserData.GetRecord(0, "AccessLPU")) < int(config["ACCESS"]["AccessLPU"])
+ or int(UserData.GetRecord(0, "AccessUOG")) < int(config["ACCESS"]["AccessUOG"])
+ or int(UserData.GetRecord(0, "AccessENP")) < int(config["ACCESS"]["AccessENP"])
+ or int(UserData.GetRecord(0, "AccessSMS")) < int(config["ACCESS"]["AccessSMS"])
+ or int(UserData.GetRecord(0, "AccessPostCard")) < int(config["ACCESS"]["AccessPostCard"])
+ or int(UserData.GetRecord(0, "AccessPhone")) < int(config["ACCESS"]["AccessPhone"])
+ or int(UserData.GetRecord(0, "AccessDial")) < int(config["ACCESS"]["AccessDial"])
+ or int(UserData.GetRecord(0, "AccessAnkets")) < int(config["ACCESS"]["AccessAnkets"])
+
+ ):
+ return render_template('error_403.htm', head=APIC.UserHead(Var) # Подключаем раздел head со скриптами и css
+ , UserHeader=APIC.UserErrorHeader(SessionID, Var) # Рисуем шапку на странице
+ , ThreadVars_message="Извините, но вам не хватает доступа к этой странице.")
+
+ import importlib.util
+ spec = importlib.util.spec_from_file_location("module.name", "app/pages/" + CurPage + "/source.py")
+ CurPage = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(CurPage)
+ return CurPage.Main(SessionID)
+ except Exception as E:
+ return render_template('error_500.htm', head=APIC.UserHead(Var) # Подключаем раздел head со скриптами и css
+ , UserHeader=APIC.UserHeader(SessionID, Var) # Рисуем шапку на странице
+ , ThreadVars_message=f"При открытии страницы {CurPage} произошла ошибка: {E}")
diff --git a/app/source/Pages.py b/app/source/Pages.py
new file mode 100644
index 0000000..5a94d85
--- /dev/null
+++ b/app/source/Pages.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Дата оптимизации: 09.06.2022
+Дата тестирования: 14.06.2022
+"""
+import os
+
+# Активация FLASK
+from app import app # чтение из config.py
+from flask import render_template # использование шаблонов
+# from flask import request # получение данных Cookie, GET и POST
+from flask import jsonify # ответ в формате JSON
+# from flask import redirect # Код страницы и перенаравление
+# import json
+import sys
+import configparser
+import importlib.util
+
+# Подключаем свои библиотеки
+sys.path.append("app/source")
+import API_Common as APIC
+import API_App
+
+
+
+###############################################################################
+# Проверка сессии
+###############################################################################
+def CheckSession(SessionID):
+ if SessionID is not None:
+ if (SessionID != ""):
+ CurDataSession: APIC.DataSession = APIC.CheckSession(SessionID, True)
+ if (CurDataSession.status == False): return False
+
+ CurSessionID = CurDataSession.sessionid
+
+ # Если нет в базе такой сессии, то заного авторизуемся
+ if (CurSessionID == ""): return False
+
+ return True
+ else:
+ return False
+ else:
+ return False
+
+
+###############################################################################
+# POST
+###############################################################################
+def API(SessionID, CurPage):
+ config = configparser.ConfigParser()
+ config.read("app/pages/" + CurPage + "/config.ini")
+
+ VarArray = {}
+ if (CheckSession(SessionID) == False) and config["ACCESS"]["NeedAuth"] == 1:
+ return "Требуется повторная авторизация. Нажмите F5.", 401
+
+ if (CurPage != ""):
+ spec = importlib.util.spec_from_file_location("module.name", f"app/pages/{CurPage}/source.py")
+ CurPage = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(CurPage)
+ return CurPage.API(SessionID)
+
+ VarArray.update({'status': 'Неизвестная операция'})
+ return jsonify(VarArray)
+
+
+###############################################################################
+# MAIN
+###############################################################################
+def Main(SessionID, CurPage):
+ # Объявление переменных
+ Var = {"Title": "", "ProgName": app.config['PROGNAME']}
+ Modal_Vars = {}
+
+ S = APIC.CheckSession(SessionID, False)
+
+ # Получаем уровень доступа
+ if (S.userid not in ("-1", "")):
+ pass # Пока не проверяем
+
+ # Создаем страничку
+ try:
+ if (CurPage is None):
+ return render_template('Pages.htm', head=APIC.UserHead(Var) # Подключаем раздел head со скриптами и css
+ , UserHeader=APIC.UserHeader(SessionID, Var) # Рисуем шапку на странице
+ , Modals=APIC.Modals(Modal_Vars) # Модальные окна
+ , ThreadVars_message=APIC.ThreadVars_Message() #
+ , version=app.config['VERSION'] #
+ , R_category="{{R.category}}" #
+ , R_name="{{R.name}}" #
+ , R_description="{{R.description}}" #
+ , R_autor="{{R.autor}}" #
+ , R_tags="{{R.tags}}" #
+ , CurTag="{{CurTag}}" #
+ )
+
+ # Если вошли в режиме Установщика то проверку не проверяем
+ if (S.userid != "-1"):
+ # Проверка прав на модуль
+ if (os.path.isfile("app/pages/" + CurPage + "/config.ini") == False):
+ return render_template('error_403.htm', head=APIC.UserHead(Var) # Подключаем раздел head со скриптами и css
+ , UserHeader=APIC.UserErrorHeader(SessionID, Var) # Рисуем шапку на странице
+ , ThreadVars_message=f"Не удалось получить настройки для страницы {CurPage}. Похоже, эта страница на стадии разработки")
+
+ config = configparser.ConfigParser()
+ config.read("app/pages/" + CurPage + "/config.ini")
+
+ if (config["ACCESS"]["UserInterFace"] not in ("0", "")):
+ if (
+
+
+ ):
+ return render_template('error_403.htm', head=APIC.UserHead(Var) # Подключаем раздел head со скриптами и css
+ , UserHeader=APIC.UserErrorHeader(SessionID, Var) # Рисуем шапку на странице
+ , ThreadVars_message="Извините, но вам не хватает доступа к этой странице.")
+
+ import importlib.util
+ spec = importlib.util.spec_from_file_location("module.name", "app/pages/" + CurPage + "/source.py")
+ CurPage = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(CurPage)
+ return CurPage.Main(SessionID)
+ except Exception as E:
+ return render_template('error_500.htm', head=APIC.UserHead(Var) # Подключаем раздел head со скриптами и css
+ , UserHeader=APIC.UserHeader(SessionID, Var) # Рисуем шапку на странице
+ , ThreadVars_message=f"При открытии страницы {CurPage} произошла ошибка: {E}")
diff --git a/app/static/image/rustdesk/rd1.png b/app/static/image/rustdesk/rd1.png
index 87b76e1..dd359c2 100644
Binary files a/app/static/image/rustdesk/rd1.png and b/app/static/image/rustdesk/rd1.png differ
diff --git a/app/static/image/rustdesk/rd2.png b/app/static/image/rustdesk/rd2.png
index 6e3623d..ffdf5fc 100644
Binary files a/app/static/image/rustdesk/rd2.png and b/app/static/image/rustdesk/rd2.png differ
diff --git a/app/static/image/rustdesk/rd3.png b/app/static/image/rustdesk/rd3.png
index c089b8e..180f035 100644
Binary files a/app/static/image/rustdesk/rd3.png and b/app/static/image/rustdesk/rd3.png differ
diff --git a/app/static/image/rustdesk/rd4.png b/app/static/image/rustdesk/rd4.png
index 9d1ad1c..7dee7f9 100644
Binary files a/app/static/image/rustdesk/rd4.png and b/app/static/image/rustdesk/rd4.png differ
diff --git a/app/static/image/rustdesk/rd5.png b/app/static/image/rustdesk/rd5.png
new file mode 100644
index 0000000..d8ef690
Binary files /dev/null and b/app/static/image/rustdesk/rd5.png differ
diff --git a/app/static/image/rustdesk/rd6.png b/app/static/image/rustdesk/rd6.png
new file mode 100644
index 0000000..fac8e04
Binary files /dev/null and b/app/static/image/rustdesk/rd6.png differ
diff --git a/app/static/image/rustdesk/rd8.png b/app/static/image/rustdesk/rd8.png
new file mode 100644
index 0000000..4a3cbae
Binary files /dev/null and b/app/static/image/rustdesk/rd8.png differ
diff --git a/app/static/image/rustdesk/rd9.png b/app/static/image/rustdesk/rd9.png
new file mode 100644
index 0000000..d582482
Binary files /dev/null and b/app/static/image/rustdesk/rd9.png differ
diff --git a/app/templates/block_head.htm b/app/templates/block_head.htm
new file mode 100644
index 0000000..c499ec6
--- /dev/null
+++ b/app/templates/block_head.htm
@@ -0,0 +1,18 @@
+
+ {{ProgName}} - {{Title}}
+
+
+
+
diff --git a/app/templates/_header b/app/templates/block_header.htm
similarity index 100%
rename from app/templates/_header
rename to app/templates/block_header.htm
diff --git a/app/templates/error_403.htm b/app/templates/error_403.htm
new file mode 100644
index 0000000..eee9c0c
--- /dev/null
+++ b/app/templates/error_403.htm
@@ -0,0 +1,17 @@
+
+
+
+ {{head|safe}}
+
+
+ {{UserHeader|safe}}
+ 
+
+
+ {{ThreadVars_message}}
+
+
+
+
+
+
diff --git a/app/templates/error_404.htm b/app/templates/error_404.htm
new file mode 100644
index 0000000..eee9c0c
--- /dev/null
+++ b/app/templates/error_404.htm
@@ -0,0 +1,17 @@
+
+
+
+ {{head|safe}}
+
+
+ {{UserHeader|safe}}
+ 
+
+
+ {{ThreadVars_message}}
+
+
+
+
+
+
diff --git a/app/templates/error_500.htm b/app/templates/error_500.htm
new file mode 100644
index 0000000..91b2b4d
--- /dev/null
+++ b/app/templates/error_500.htm
@@ -0,0 +1,17 @@
+
+
+
+ {{head|safe}}
+
+
+ {{UserHeader|safe}}
+ 
+
+
+ {{ThreadVars_message}}
+
+
+
+
+
+
diff --git a/app/templates/rustdesk.htm b/app/templates/rustdesk.htm
deleted file mode 100644
index 19245d8..0000000
--- a/app/templates/rustdesk.htm
+++ /dev/null
@@ -1,118 +0,0 @@
-
-
-
-
-
- {{ProgName}} - {{Title}}
-
-
-
-
-
-
-
-
- {{UserHeader|safe}}
-
-
-
- 1. Зайти и скачать дистрибутив с сайта RustDesk или скачать:
-
-
-
- 2. Напротив ID нажать на троеточие и выбрать пункт “ID/Сервер ретрансляции”
-

- 3. Указать следующие настройки:
-
-
-
-
-
-
-
- 
-
-
- 4. Сообщить ваш ID для подключения.
-
-
-
- 5. Нажать "Принять" в появившимся верхнем правом углу.
-
- 
-
- {{UserBottom|safe}}
-
-
-
\ No newline at end of file
diff --git a/app/views.py b/app/views.py
index ab9670c..ae6aa6a 100644
--- a/app/views.py
+++ b/app/views.py
@@ -5,14 +5,19 @@
from app import app # чтение из config.py, роуты
from flask import render_template # использование шаблонов
from flask import request # получение данных Cookie, GET и POST
-from flask import redirect # Код страницы и перенаравление
import os # чтение куки и работа с файлами и папками
+import sys # Подключаем свои библиотеки
+
+sys.path.append("app/source")
+import Pages # Страницы сайта
+import API_Common as APIC
# Глобальные переменные
ProgName = app.config['PROGNAME']
version = app.config['VERSION']
-
+appname = app.config['SHMNAME']
+"""
LevelA = ""
Flag = False
FIO = ""
@@ -23,10 +28,10 @@ CurSessionID = ""
S = ['0', '1', '2', '3']
+"""
+
###############################################################################
-
-
@app.errorhandler(413)
def request_entity_too_large(error):
return 'Превышен максимальный размер файла', 413
@@ -38,22 +43,6 @@ def page_not_found(e):
return """Здесь нет того, чего ты ищешь...
Вернутся на главную """, 404
-@app.errorhandler(403)
-def access_denided(e):
- # Получаем куки и сессию
-
- SessionID = request.cookies.get('SessionID')
- print(SessionID)
-
- if SessionID is None or SessionID == "": # если нет сессии
- return redirect("/login", code=302)
- else:
- HTML = """Похоже, сюда можно только администраторам.
Вернутся на главную """
- return render_template('index.htm', UserHeader=APIC.UserHeader(SessionID, "Доступ запрещен", ProgName), HTML=HTML)
-
- # return render_template('404.html'), 404
-
-
###############################################################################
# Добавляем роуты
@app.route('/')
@@ -67,13 +56,18 @@ def main():
)
-###############################################################################
-@app.route('/rustdesk')
-def rustdesk():
- Title = "RustDesk"
- return render_template('rustdesk.htm',
- UserHeader=UserHeader(Title, ProgName), version=version, Title=Title, ProgName=ProgName
- )
+########################################################################
+# rustdesk
+########################################################################
+@app.route('/rustdesk', methods=['GET', 'POST'])
+def route_rustdesk():
+ # Получаем куки и сессию
+ SessionID = request.cookies.get(appname + '_SessionID')
+
+ if request.method == 'POST':
+ return Pages.API(SessionID, "rustdesk") # Если POST запрос
+ else:
+ return Pages.Main(SessionID, "rustdesk") # Если GET запрос
###############################################################################
@@ -98,8 +92,8 @@ def portfolio():
###############################################################################
def UserHeader(Title, ProgName):
CurStr = ""
- if (os.path.exists("app/templates/_header") == True):
- file = open("app/templates/_header", "r")
+ if (os.path.exists("app/templates/block_header.htm") == True):
+ file = open("app/templates/block_header.htm", "r")
CurStr = file.read()
file.close()
diff --git a/shell/1a-CheckSystem.sh b/shell/1a-CheckSystem.sh
index bf3b1cf..9f87d98 100755
--- a/shell/1a-CheckSystem.sh
+++ b/shell/1a-CheckSystem.sh
@@ -20,22 +20,22 @@ CheckPython3()
{
echo ""
echo "============================================================="
- echo "Python 3.10.12"
+ echo "Python 3.10.13"
echo "============================================================="
echo "Проверяем наличие Python3 в системе "
- T=$(python3 -V | grep 3.10.12)
+ T=$(python3 -V | grep 3.10.13)
if [ "$T" == "" ];
then
echo "Устанавливаем зависимости для Python 3.10 "
sudo apt-get build-dep python3.10
- echo "Скачиваем Python 3.10.12 "
+ echo "Скачиваем Python 3.10.13 "
cd /tmp/
- wget -c https://www.python.org/ftp/python/3.10.12/Python-3.10.12.tgz
+ wget -c https://www.python.org/ftp/python/3.10.13/Python-3.10.13.tgz
echo "Распаковываем архив... "
- tar -xvzf Python-3.10.12.tgz
- cd Python-3.10.12
+ tar -xvzf Python-3.10.13.tgz
+ cd Python-3.10.13
echo "Настраиваем пакет... "
./configure --enable-optimizations
@@ -44,15 +44,15 @@ CheckPython3()
make
make test
- echo "Устанавливаем Python 3.10.12... "
+ echo "Устанавливаем Python 3.10.13... "
sudo make install
else
- echo "Уже установлен Python 3.10.12"
+ echo "Уже установлен Python 3.10.13"
return 1
fi
echo "Перепроверяем " $1
- T=$(python3 -V | grep 3.10.12)
+ T=$(python3 -V | grep 3.10.13)
if $T= ""
then
echo "Не удалось установить " $1
diff --git a/shell/r.txt b/shell/r.txt
index 3424dda..0f472c1 100644
--- a/shell/r.txt
+++ b/shell/r.txt
@@ -2,4 +2,4 @@ Flask
Flask-Compress
Flask-Cors
gunicorn
-
+requests