k2inst.py 17.9 KB
import zipfile
from flask import Flask, Blueprint, jsonify, render_template, redirect, url_for, session, g
import os
import shutil
from datetime import datetime
import uuid
from flask import request
from k2.k2cfg import k2
from flask_migrate import Migrate
import requests
import subprocess
from flask import current_app
import yaml
from functools import wraps

db = k2.db
#migrate = Migrate(app, db)
def generate_id():
    generated_id = uuid.uuid4().hex[:32]
    return ''.join([generated_id[i:i + 4] for i in range(0, len(generated_id), 4)])
class Component(db.Model):
    __bind_key__ = 'db2'
    __tablename__ = 'component'
    __table_args__ = {'extend_existing': True}
    id = db.Column(db.String(36), primary_key=True, default=generate_id)
    name = db.Column(db.String(100), nullable=False)
    description = db.Column(db.String(200))
    version = db.Column(db.String(20))
    git_link = db.Column(db.String(200))
    dependencies = db.Column(db.String)
    date_installed = db.Column(db.DateTime, default=datetime.utcnow)
    installed = db.Column(db.Boolean, default=False)

    def __init__(self, name, description, version, git_link,  dependencies, installed):
        self.name = name
        self.description = description
        self.version = version
        self.git_link = git_link
        self.dependencies = dependencies
        self.installed = installed
    def __repr__(self):
        return f"Component('{self.name}', '{self.version}')"
    @classmethod
    def get_repository_by_id(self,rep_id):
        component = db.session.query(Component).filter(Component.id == rep_id).first()
        return component
components_bp = Blueprint('components', __name__, template_folder='templates')
config = yaml.safe_load(open("db/first-login.yml"))

def first_login_required(view_func):
    @wraps(view_func)
    def decorated_view(*args, **kwargs):
        if 'username' not in session:
            return redirect(url_for('first_login'))
        return view_func(*args, **kwargs)

    return decorated_view


@components_bp.route('/api/languages', methods=['GET'])
def find_language():
    components = Component.query.all()
    languages_paths = []
    # Додаємо головну директорію languages
    languages_paths.append('languages')
    # Знаходимо шляхи до директорій languages всередині папки components
    for component in components:
        component_languages_directory = 'components/' + component.name + '/' + component.name + '/languages'
        languages_paths.append(component_languages_directory)
    result = ';'.join(languages_paths)
    print(result)
    return result

@components_bp.route('/', methods=['GET', 'POST'])
@first_login_required
def home():
    return redirect('/dashboard')

@components_bp.route('/login', methods=['GET', 'POST'])
def first_login():
    if request.method == "POST":
        username = request.form.get("username")
        password = request.form.get("password")
        for user in config["users"]:
            if user["username"] == username and user["password"] == password:
                session['username'] = username  # Збереження ім'я користувача в сесії
                return redirect(url_for('components.dashboard'))
        return "Невірне ім'я користувача або пароль."
    return render_template('first-login.html')

@components_bp.route('/logout')
def logout():
    session.pop('username', None)  # Видалення ім'я користувача з сесії
    return redirect(url_for('first_login'))

@components_bp.route('/change_language/<lang>')
def change_language(lang):
    session['lang'] = lang
    k2.current_language = lang
    return redirect(url_for('components.dashboard'))

@components_bp.route('/dashboard')
@first_login_required
def dashboard():

        # Компоненти доступні для встановлення
    try:
        # GET-запит до API
        response = requests.get('http://127.0.0.1:8001/api/components')
        json_data = response.json()
        # Перетворення JSON-об'єкту на масив
        component_server = [item for item in json_data]
    except:
        component_server = None

    # Встановлені компоненти
    components = Component.query.all()
    components_names = [component.name for component in components]
    # Отримання значення пошукового запиту з параметрів URL
    search_query = request.args.get('search_query')
    #print(search_query)
    filtered_components = []
    if search_query:
        filtered_components = [component for component in component_server if
                               (search_query.lower() in component['name'].lower() if component['name'] else False) or
                               (search_query.lower() in component['description'].lower() if component[
                                   'description'] else False)]
    else:
        filtered_components = component_server
    current_language = k2.current_language

    return render_template('dashboard.html', components=components,
                           components_names=components_names, component_server=filtered_components,
                           search_query=search_query, language=current_language )

@components_bp.route('/show_components/<string:component_id>')
@first_login_required
def show_components(component_id):
    response = requests.get('http://127.0.0.1:8001/api/components')
    json_data = response.json()
    selected_component = next((component for component in json_data if component['id'] == component_id), None)
    return render_template('component-info.html', selected_component=selected_component)

@components_bp.route('/install_components_git/<string:component_id>')
@first_login_required
def install_components_git(component_id):
    # Шлях до головної папки проекту
    project_folder = 'components'
    #component = Component.get_repository_by_id(component_id)
    response = requests.get('http://127.0.0.1:8001/api/components')
    json_data = response.json()
    selected_component = next((component for component in json_data if component['id'] == component_id), None)
    # Назва репозиторія
    repository_name = selected_component['name']
    # Шлях до папки репозиторія в межах проекту
    repository_folder = os.path.join(project_folder, repository_name)
    # URL репозиторія

    git_repo_url = selected_component['git_link']
    try:
        # Перевірка наявності папки репозиторія
        if not os.path.exists(repository_folder):
            # Створення папки репозиторія
            os.makedirs(repository_folder)
        # Шлях до файлу __init__.py
        init_file = os.path.join(repository_folder, '__init__.py')
        # Перевірка наявності файлу __init__.py
        if not os.path.exists(init_file):
            # Створення пустого файлу __init__.py
            open(init_file, 'a').close()
        #підключення до приватного репозиторію
        #os.environ['GITLAB_PRIVATE_TOKEN'] = '8xxTpxrKVDGoXD5ynjiW'
        #git_repo_url_with_token = git_repo_url.replace('https://',
        #                                               f'https://oauth2:{os.environ["GITLAB_PRIVATE_TOKEN"]}@')

        # Команда для встановлення з використанням git_repo_url і повного шляху до папки репозиторія
        command = ['venv/Scripts/python.exe', '-m', 'pip', 'install', '--use-pep517', 'git+' + git_repo_url, '--target=' + repository_folder]
        # Виконуємо команду встановлення
        subprocess.check_call(command)

        # Шлях до файлу, до якого потрібно додати код
        file_path = 'routes.py'
        # Код, який потрібно додати
        code = selected_component['dependencies']
        # Відкриття файлу у режимі дозапису
        with open(file_path, 'a') as file:
            # Запис нового коду у файл
            file.write('\n')
            file.write(code)
            file.write('\n')
        component =  Component.query.filter_by(name=selected_component['name']).first()
        if not component:
            new_component = Component(
                    name=selected_component['name'],
                    description=selected_component['description'],
                    version=selected_component['version'],
                    git_link=selected_component['git_link'],
                    dependencies=selected_component['dependencies'],
                    installed=True
                )
            # Add the new component to the database
            db.session.add(new_component)
            db.session.commit()

        # Повертаємо повідомлення про успішне встановлення
        return f'Installed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />'
    except subprocess.CalledProcessError as e:
        return 'Error installing : ' + str(e)

@components_bp.route('/install_components/<string:component_id>')
@first_login_required
def install_component_from_archive(component_id):
    # Шлях до головної папки проекту
    project_folder = 'components'
    # Отримати відповідну компоненту зі списку компонент
    response = requests.get('http://127.0.0.1:8001/api/components')
    json_data = response.json()
    selected_component = next((component for component in json_data if component['id'] == component_id), None)
    if selected_component is None:
        return 'Component not found'
    # Отримати посилання на архів компоненти та версію
    archive_url = selected_component['latest_component_data']
    version = selected_component['latest_version']
    try:
        # Створити шлях до папки компоненти згідно назви та версії
        component_folder = os.path.join(project_folder)
        os.makedirs(component_folder, exist_ok=True)
        # Завантажити архів компоненти
        response = requests.get(archive_url, stream=True)
        response.raise_for_status()
        # Шлях до завантаженого архіву
        archive_path = os.path.join(component_folder, f"{selected_component['name']}.zip")
        # Встановити залежності з файлу requirements.txt

        # Зберегти архів на диск
        with open(archive_path, "wb") as file:
            for chunk in response.iter_content(chunk_size=8192):
                file.write(chunk)
        # Розпакувати архів
        with zipfile.ZipFile(archive_path, "r") as zip_ref:
            zip_ref.extractall(component_folder)

        # Видалити архів
        os.remove(archive_path)

        # Шлях до файлу, до якого потрібно додати код
        file_path = 'routes.py'
        # Код, який потрібно додати
        code = f'''from components.{selected_component['name']}.{selected_component['name']}.views import {selected_component['name']}\napp.register_blueprint({selected_component['name']}, url_prefix='/{selected_component['name']}')'''
        #selected_component['dependencies']

        # Відкриття файлу у режимі дозапису
        with open(file_path, 'a') as file:
            # Запис нового коду у файл
            file.write('\n')
            file.write(code)
            file.write('\n')

        # Оновити базу даних з встановленою компонентою
        component = Component.query.filter_by(name=selected_component['name']).first()
        if not component:
            component = Component(
                name=selected_component['name'],
                description=selected_component['description'],
                version=version,
                git_link=selected_component['git_link'],
                dependencies=code,
                installed=True
            )
            db.session.add(component)
        else:
            component.version = version
            component.git_link = selected_component['git_link']
            component.dependencies = code
            component.installed = True

        db.session.commit()


        k2.search_babel_translation_directories()

        return f'''Component installed successfully: {selected_component["name"]} v{version} {k2.babel_translation_directories},
                \n \n please wait installing requirments...     
                <meta http-equiv="refresh" content="0;url=/install-requirments/{selected_component["name"]}" />'''

    except Exception as e:
        return f'Error installing component: {str(e)}'

@components_bp.route('/install-requirments/<string:selected_component_name>', methods=['GET'])
@first_login_required
def install_requirments(selected_component_name):
    requirements_file = os.path.join('components', selected_component_name, "requirements.txt")
    if os.path.isfile(requirements_file):
        pip_command = f"{k2.venv_bin_path}/python -m pip install -r {requirements_file}"
        subprocess.run(pip_command, shell=True, check=True)
    return f'''Requirements installed successfully
     \n \n please wait installing requirments components...     
     <meta http-equiv="refresh" content="1;url=/install-requirments-components" />'''


@components_bp.route('/install-requirments-components')
@first_login_required
def install_requirements_components():
    # Откриття файлу requirements_components.txt
    requirements_file = 'requirements_components.txt'
    component_ids = None
    if os.path.isfile(requirements_file):
        with open('requirements_components.txt', 'r') as file:
            component_ids = file.read().splitlines()
    if component_ids:
        for component_id in component_ids:
            # Виклик роуту '/install_components/<string:component_name>' для кожної назви компоненти
            response = requests.get(
                f'/install_components/{component_id}')
        return f'Requirements  components  installed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />'
    else:
        return f'<meta http-equiv="refresh" content="1;url=/dashboard" />'
            # Опрацювання відповіді (за потреби)


@components_bp.route('/remove_dependencies/<string:component_id>', methods=['GET'])
@first_login_required
def remove_dependencies(component_id):
    # Знаходимо компоненту за її ID
    component = Component.query.get(component_id)
    if not component:
        return 'Component not found <meta http-equiv="refresh" content="1;url=/dashboard" />'

    # Шлях до файлу, з якого потрібно видалити залежності
    file_path = 'routes.py'

    # Зчитуємо вміст файлу
    with open(file_path, 'r') as file:
        lines = file.readlines()

    # Видаляємо рядки, що містять залежності компоненти
    updated_lines = [line for line in lines if line.strip() not in component.dependencies]

    # Записуємо оновлений вміст у файл
    with open(file_path, 'w') as file:
        file.writelines(updated_lines)

    # Оновлюємо статус компоненти
    component.installed = False
    db.session.commit()

    return 'Component successfully turn off <meta http-equiv="refresh" content="1;url=/dashboard" />'

@components_bp.route('/add_dependencies/<string:component_id>', methods=['GET'])
@first_login_required
def add_dependencies(component_id):

    # Знаходимо компоненту за її ID
    component = Component.query.get(component_id)
    if not component:
        return 'Component not found <meta http-equiv="refresh" content="1;url=/dashboard" />'

    # Шлях до файлу, з якого потрібно видалити залежності
    file_path = 'routes.py'
    code = component.dependencies
    # Відкриття файлу у режимі дозапису
    with open(file_path, 'a') as file:
        # Запис нового коду у файл
        file.write(code)

    # Оновлюємо статус компоненти
    component.installed = True
    db.session.commit()
    return 'Dependencies added successfully <meta http-equiv="refresh" content="1;url=/dashboard" />'

@components_bp.route('/remove-component/<string:component_id>')
@first_login_required
def remove_component(component_id):
    # Шлях до головної папки проекту
    project_folder = 'components'
    component = Component.query.get(component_id)
    # Назва репозиторія
    repository_name = component.name
    # Шлях до папки репозиторія в межах проекту
    repository_folder = os.path.join(project_folder, repository_name)
    try:
        # Перевірка наявності папки репозиторія
        if os.path.exists(repository_folder):
            # Видалення папки репозиторія
            shutil.rmtree(repository_folder)
        # Видаляємо компоненту з бази
        if component:
            db.session.delete(component)
            db.session.commit()

        # Повертаємо повідомлення про успішне видалення
        return 'Component removed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />'
    except Exception as e:
        # Повертаємо повідомлення про помилку видалення
        return 'Error removing component: ' + str(e)