Compare View
Commits (5)
Changes
Showing 56 changed files Side-by-side Diff
- .gitignore
- __pycache__/main.cpython-310.pyc
- __pycache__/routes.cpython-310.pyc
- components/__pycache__/__init__.cpython-310.pyc
- components/adm/README.md
- components/adm/__init__.py
- components/adm/adm/.idea/.gitignore
- components/adm/adm/.idea/adm.iml
- components/adm/adm/.idea/inspectionProfiles/Project_Default.xml
- components/adm/adm/.idea/inspectionProfiles/profiles_settings.xml
- components/adm/adm/.idea/misc.xml
- components/adm/adm/.idea/modules.xml
- components/adm/adm/.idea/vcs.xml
- components/adm/adm/__init__.py
- components/adm/adm/__pycache__/__init__.cpython-310.pyc
- components/adm/adm/__pycache__/__init__.cpython-311.pyc
- components/adm/adm/__pycache__/models.cpython-310.pyc
- components/adm/adm/__pycache__/views.cpython-310.pyc
- components/adm/adm/__pycache__/views.cpython-311.pyc
- components/adm/adm/models.py
- components/adm/adm/views.py
- components/adm/requirements.txt
- components/adm/setup.py
- components/k2test/__pycache__/__init__.cpython-310.pyc
- components/k2test/history.txt
- components/k2test/k2test/__pycache__/__init__.cpython-310.pyc
- components/k2test/k2test/__pycache__/views.cpython-310.pyc
- components/k2test/k2test/languages/en/LC_MESSAGES/messages.mo
- components/k2test/k2test/languages/en/LC_MESSAGES/messages.po
- components/k2test/k2test/languages/uk/LC_MESSAGES/messages.mo
- components/k2test/k2test/languages/uk/LC_MESSAGES/messages.po
- components/k2test/k2test/static/default.css
- components/k2test/k2test/static/style.css
- components/k2test/k2test/templates/login.html
- components/k2test/k2test/views.py
- components/k2test/menu_items.yml
- components/k2test/setup.py
- components/menu.yml
- db/database.db
- k2/__pycache__/__init__.cpython-310.pyc
- k2/__pycache__/k2cfg.cpython-310.pyc
- k2/__pycache__/k2comp.cpython-310.pyc
- k2/__pycache__/k2obj.cpython-310.pyc
- k2/__pycache__/k2rout.cpython-310.pyc
- k2/k2cfg.py
- k2/k2comp.py
- k2/k2inst.py
- k2/k2obj.py
- k2/k2rout.py
- languages/babel_translation_directories.yml
- main.py
- requirements.txt
- routes.py
- templates/dashboard.html
- templates/first-login.html
- templates/index.html
.gitignore
__pycache__/main.cpython-310.pyc
No preview for this file type
__pycache__/routes.cpython-310.pyc
No preview for this file type
components/__pycache__/__init__.cpython-310.pyc
No preview for this file type
components/k2test/__pycache__/__init__.cpython-310.pyc
No preview for this file type
components/k2test/history.txt
components/k2test/k2test/__pycache__/__init__.cpython-310.pyc
No preview for this file type
components/k2test/k2test/__pycache__/views.cpython-310.pyc
No preview for this file type
components/k2test/k2test/languages/en/LC_MESSAGES/messages.mo
No preview for this file type
components/k2test/k2test/languages/en/LC_MESSAGES/messages.po
| 1 | -msgid "Installed components" | |
| 2 | -msgstr "Installed components" | |
| 3 | - | |
| 4 | -msgid "Name" | |
| 5 | -msgstr "Name" | |
| 6 | - | |
| 7 | -msgid "Description" | |
| 8 | -msgstr "Description" | |
| 9 | - | |
| 10 | -msgid "Version" | |
| 11 | -msgstr "Version" | |
| 12 | - | |
| 13 | -msgid "Dependencies" | |
| 14 | -msgstr "Dependencies" | |
| 15 | - | |
| 16 | -msgid "Git Link" | |
| 17 | -msgstr "Git Link" | |
| 18 | - | |
| 19 | -msgid "Install At" | |
| 20 | -msgstr "Install At" | |
| 21 | - | |
| 22 | -msgid "Status" | |
| 23 | -msgstr "Status" | |
| 24 | - | |
| 25 | -msgid "Action" | |
| 26 | -msgstr "Action" | |
| 27 | - | |
| 28 | -msgid "Components Shop" | |
| 29 | -msgstr "Components Shop" | |
| 30 | - | |
| 31 | 1 | msgid "New 2 page" |
| 32 | -msgstr "New 2 page" | |
| 2 | +msgstr "Нова сторінка 2" | |
| 3 | +msgid "title_menu_items" | |
| 4 | +msgstr "Test menu items" | |
| 33 | 5 | \ No newline at end of file |
components/k2test/k2test/languages/uk/LC_MESSAGES/messages.mo
No preview for this file type
components/k2test/k2test/languages/uk/LC_MESSAGES/messages.po
components/k2test/k2test/views.py
| 1 | 1 | from flask import render_template, redirect, request, jsonify |
| 2 | +from flask_babel import gettext | |
| 2 | 3 | from flask_login import login_user, current_user, logout_user, login_required |
| 3 | 4 | from flask import current_app as k2 |
| 4 | 5 | from flask import Blueprint, json |
| 5 | 6 | from flask_sqlalchemy import SQLAlchemy |
| 6 | 7 | |
| 7 | 8 | |
| 9 | +#bliueprint | |
| 10 | +k2test = Blueprint('k2test', __name__, template_folder='templates', static_folder='static') | |
| 8 | 11 | |
| 9 | -k2test = Blueprint('k2test', __name__, template_folder='templates', static_folder='static',) | |
| 10 | - | |
| 11 | - | |
| 12 | - | |
| 12 | +#component routes | |
| 13 | 13 | @k2test.route('/new') |
| 14 | 14 | def new_component(): |
| 15 | 15 | db = k2.extensions['sqlalchemy'].db |
| ... | ... | @@ -33,12 +33,12 @@ def new_component3(): |
| 33 | 33 | def new_component4(): |
| 34 | 34 | return jsonify({'data': 'page new4'}) |
| 35 | 35 | |
| 36 | - | |
| 37 | -@k2test.route('/menu-items') | |
| 36 | +# menu items for admin | |
| 37 | +@k2test.route('/menu-admin-items') | |
| 38 | 38 | def menu_items(): |
| 39 | 39 | data = [ |
| 40 | 40 | { |
| 41 | - 'title': 'Тестова компонента', | |
| 41 | + 'title': f"{gettext('title_menu_items')}", | |
| 42 | 42 | 'icon': {'icon': 'mdi-chart-timeline-variant'}, |
| 43 | 43 | 'children': [ |
| 44 | 44 | {'title': 'Тест 3', 'to': 'k2test-new3'}, |
| ... | ... | @@ -47,4 +47,5 @@ def menu_items(): |
| 47 | 47 | } |
| 48 | 48 | |
| 49 | 49 | ] |
| 50 | - return data | |
| 51 | 50 | \ No newline at end of file |
| 51 | + return data | |
| 52 | + |
components/k2test/setup.py
| ... | ... | @@ -4,8 +4,8 @@ setup( |
| 4 | 4 | name='k2test', |
| 5 | 5 | version='1.0.0', |
| 6 | 6 | description='Description of my project', |
| 7 | - author='Your Name', | |
| 8 | - author_email='yourname@example.com', | |
| 7 | + author='k2 cloud', | |
| 8 | + author_email='rs@corp2.net', | |
| 9 | 9 | keywords="k2test, k2", |
| 10 | 10 | python_requires=">=3.7, <=3.11.2", |
| 11 | 11 | packages=['k2test'], |
db/database.db
No preview for this file type
k2/__pycache__/__init__.cpython-310.pyc
No preview for this file type
k2/__pycache__/k2cfg.cpython-310.pyc
No preview for this file type
k2/__pycache__/k2comp.cpython-310.pyc
No preview for this file type
k2/__pycache__/k2obj.cpython-310.pyc
No preview for this file type
k2/__pycache__/k2rout.cpython-310.pyc
No preview for this file type
k2/k2cfg.py
| ... | ... | @@ -9,11 +9,13 @@ import os |
| 9 | 9 | import sys |
| 10 | 10 | from flask import session |
| 11 | 11 | |
| 12 | + | |
| 13 | + | |
| 12 | 14 | class k2(k2obj): |
| 13 | 15 | version = '2.5.1.27' |
| 14 | 16 | db = SQLAlchemy() |
| 15 | 17 | domain = 'http://127.0.0.1:5000/' |
| 16 | - update_domain ='http://127.0.0.1:8001/' | |
| 18 | + update_domain ='http://127.0.0.1:8002/' | |
| 17 | 19 | venv_bin_path = os.path.join(os.path.dirname(sys.prefix), 'venv/Scripts') |
| 18 | 20 | json_sort_keys = False |
| 19 | 21 | timezone = pytz.timezone('Europe/Kiev') |
| ... | ... | @@ -21,6 +23,9 @@ class k2(k2obj): |
| 21 | 23 | default_language = 'en' |
| 22 | 24 | current_language = '' |
| 23 | 25 | babel_translation_directories = '' |
| 26 | + components = '' | |
| 27 | + | |
| 28 | + | |
| 24 | 29 | |
| 25 | 30 | def __init__(self): |
| 26 | 31 | super().__init__() |
| ... | ... | @@ -52,12 +57,6 @@ class k2(k2obj): |
| 52 | 57 | |
| 53 | 58 | with open('languages/babel_translation_directories.yml', 'w') as f: |
| 54 | 59 | yaml.dump(data, f) |
| 55 | - @classmethod | |
| 56 | - def get_menu_elenents(cls): | |
| 57 | - with open('components/menu.yml', 'r') as f: | |
| 58 | - yaml_data = yaml.load(f, Loader=yaml.FullLoader) | |
| 59 | - cls.menu = yaml_data | |
| 60 | - | |
| 61 | 60 | |
| 62 | 61 | # load database settings from yml file |
| 63 | 62 | def init_db_uri(self): |
| ... | ... | @@ -74,6 +73,3 @@ class k2(k2obj): |
| 74 | 73 | app.config['SQLALCHEMY_BINDS'] = {'db2': f'sqlite:///{db_path}'} |
| 75 | 74 | self.db.init_app(app) |
| 76 | 75 | |
| 77 | - | |
| 78 | - | |
| 79 | - |
k2/k2comp.py
| ... | ... | @@ -0,0 +1,40 @@ |
| 1 | +import uuid | |
| 2 | +from datetime import datetime | |
| 3 | +from .k2cfg import k2 | |
| 4 | + | |
| 5 | +db = k2.db | |
| 6 | +def generate_id(): | |
| 7 | + generated_id = uuid.uuid4().hex[:32] | |
| 8 | + return ''.join([generated_id[i:i + 4] for i in range(0, len(generated_id), 4)]) | |
| 9 | +class Component(db.Model): | |
| 10 | + __bind_key__ = 'db2' | |
| 11 | + __tablename__ = 'component' | |
| 12 | + __table_args__ = {'extend_existing': True} | |
| 13 | + id = db.Column(db.String(36), primary_key=True, default=generate_id) | |
| 14 | + name = db.Column(db.String(100), nullable=False) | |
| 15 | + description = db.Column(db.String(200)) | |
| 16 | + version = db.Column(db.String(20)) | |
| 17 | + git_link = db.Column(db.String(200)) | |
| 18 | + dependencies = db.Column(db.String) | |
| 19 | + date_installed = db.Column(db.DateTime, default=datetime.utcnow) | |
| 20 | + installed = db.Column(db.Boolean, default=False) | |
| 21 | + | |
| 22 | + def __init__(self, name, description, version, git_link, dependencies, installed): | |
| 23 | + self.name = name | |
| 24 | + self.description = description | |
| 25 | + self.version = version | |
| 26 | + self.git_link = git_link | |
| 27 | + self.dependencies = dependencies | |
| 28 | + self.installed = installed | |
| 29 | + def __repr__(self): | |
| 30 | + return f"Component('{self.name}', '{self.version}')" | |
| 31 | + @classmethod | |
| 32 | + def get_repository_by_id(self,rep_id): | |
| 33 | + component = db.session.query(Component).filter(Component.id == rep_id).first() | |
| 34 | + return component | |
| 35 | + @classmethod | |
| 36 | + def get_repository_by_id(self,rep_id): | |
| 37 | + component = db.session.query(Component).filter(Component.id == rep_id).first() | |
| 38 | + return component | |
| 39 | + | |
| 40 | + |
k2/k2obj.py
k2/k2rout.py
| ... | ... | @@ -0,0 +1,472 @@ |
| 1 | +import zipfile | |
| 2 | +from flask import Flask, Blueprint, jsonify, render_template, redirect, url_for, session, jsonify | |
| 3 | +import os | |
| 4 | +import shutil | |
| 5 | +from flask import request | |
| 6 | +from .k2cfg import k2 | |
| 7 | +from .k2comp import Component | |
| 8 | +from flask_migrate import Migrate | |
| 9 | +import requests | |
| 10 | +import subprocess | |
| 11 | +from flask import current_app | |
| 12 | +import yaml | |
| 13 | +from functools import wraps | |
| 14 | +import os | |
| 15 | +import sys | |
| 16 | + | |
| 17 | +# initialize db | |
| 18 | +# migrate = Migrate(app, db) | |
| 19 | + | |
| 20 | +components_bp = Blueprint('components', __name__, template_folder='templates') | |
| 21 | +config = yaml.safe_load(open("db/first-login.yml")) | |
| 22 | + | |
| 23 | +db = k2.db | |
| 24 | + | |
| 25 | + | |
| 26 | +def first_login_required(view_func): | |
| 27 | + @wraps(view_func) | |
| 28 | + def decorated_view(*args, **kwargs): | |
| 29 | + if 'username' not in session: | |
| 30 | + return redirect(url_for('components.first_login')) | |
| 31 | + return view_func(*args, **kwargs) | |
| 32 | + | |
| 33 | + return decorated_view | |
| 34 | + | |
| 35 | + | |
| 36 | +# def append_to_yaml_file(file_path, data): | |
| 37 | +# with open(file_path, 'r') as f: | |
| 38 | +# existing_data = yaml.safe_load(f) or [] | |
| 39 | +# | |
| 40 | +# if not existing_data: | |
| 41 | +# start_id = 1 | |
| 42 | +# else: | |
| 43 | +# start_id = max(item.get('id', 0) for item in existing_data) + 1 | |
| 44 | +# | |
| 45 | +# for i, item in enumerate(data, start=start_id): | |
| 46 | +# item['id'] = i | |
| 47 | +# existing_data.append(item) | |
| 48 | +# | |
| 49 | +# with open(file_path, 'w') as f: | |
| 50 | +# yaml.dump(existing_data, f, default_flow_style=False) | |
| 51 | + | |
| 52 | + | |
| 53 | +def remove_from_yaml_file(file_path, component_name): | |
| 54 | + with open(file_path, 'r') as f: | |
| 55 | + existing_data = yaml.safe_load(f) or [] | |
| 56 | + | |
| 57 | + updated_data = [item for item in existing_data if item.get('component_name') != component_name] | |
| 58 | + | |
| 59 | + with open(file_path, 'w') as f: | |
| 60 | + yaml.dump(updated_data, f, default_flow_style=False) | |
| 61 | + | |
| 62 | + | |
| 63 | +@components_bp.route('/api/languages', methods=['GET']) | |
| 64 | +def find_language(): | |
| 65 | + components = Component.query.all() | |
| 66 | + languages_paths = [] | |
| 67 | + # Додаємо головну директорію languages | |
| 68 | + languages_paths.append('languages') | |
| 69 | + # Знаходимо шляхи до директорій languages всередині папки components | |
| 70 | + for component in components: | |
| 71 | + component_languages_directory = 'components/' + component.name + '/' + component.name + '/languages' | |
| 72 | + languages_paths.append(component_languages_directory) | |
| 73 | + result = ';'.join(languages_paths) | |
| 74 | + print(result) | |
| 75 | + return result | |
| 76 | + | |
| 77 | + | |
| 78 | +@components_bp.route('/home', methods=['GET', 'POST']) | |
| 79 | +@first_login_required | |
| 80 | +def home(): | |
| 81 | + return redirect('/dashboard') | |
| 82 | + | |
| 83 | + | |
| 84 | +@components_bp.route('/first-login', methods=['GET', 'POST']) | |
| 85 | +def first_login(): | |
| 86 | + if request.method == "POST": | |
| 87 | + username = request.form.get("username") | |
| 88 | + password = request.form.get("password") | |
| 89 | + for user in config["users"]: | |
| 90 | + if user["username"] == username and user["password"] == password: | |
| 91 | + session['username'] = username # Збереження ім'я користувача в сесії | |
| 92 | + return redirect(url_for('components.dashboard')) | |
| 93 | + return "Невірне ім'я користувача або пароль." | |
| 94 | + return render_template('first-login.html') | |
| 95 | + | |
| 96 | + | |
| 97 | +@components_bp.route('/change_language/<lang>') | |
| 98 | +def change_language(lang): | |
| 99 | + session['lang'] = lang | |
| 100 | + k2.current_language = lang | |
| 101 | + return redirect(url_for('components.dashboard')) | |
| 102 | + | |
| 103 | + | |
| 104 | +@components_bp.route('/dashboard') | |
| 105 | +@first_login_required | |
| 106 | +def dashboard(): | |
| 107 | + # Компоненти доступні для встановлення | |
| 108 | + try: | |
| 109 | + # GET-запит до API | |
| 110 | + response = requests.get(f'{k2.update_domain}api/components') | |
| 111 | + json_data = response.json() | |
| 112 | + # Перетворення JSON-об'єкту на масив | |
| 113 | + component_server = [item for item in json_data] | |
| 114 | + except: | |
| 115 | + component_server = None | |
| 116 | + | |
| 117 | + # Встановлені компоненти | |
| 118 | + components = Component.query.all() | |
| 119 | + components_names = [component.name for component in components] | |
| 120 | + # Отримання значення пошукового запиту з параметрів URL | |
| 121 | + search_query = request.args.get('search_query') | |
| 122 | + # print(search_query) | |
| 123 | + filtered_components = [] | |
| 124 | + if search_query: | |
| 125 | + filtered_components = [component for component in component_server if | |
| 126 | + (search_query.lower() in component['name'].lower() if component['name'] else False) or | |
| 127 | + (search_query.lower() in component['description'].lower() if component[ | |
| 128 | + 'description'] else False)] | |
| 129 | + else: | |
| 130 | + filtered_components = component_server | |
| 131 | + current_language = k2.current_language | |
| 132 | + | |
| 133 | + return render_template('dashboard.html', components=components, | |
| 134 | + components_names=components_names, component_server=filtered_components, | |
| 135 | + search_query=search_query, language=k2.menu) | |
| 136 | + | |
| 137 | + | |
| 138 | +@components_bp.route('/show_components/<string:component_id>') | |
| 139 | +def show_components(component_id): | |
| 140 | + response = requests.get(f'{k2.update_domain}api/components') | |
| 141 | + json_data = response.json() | |
| 142 | + selected_component = next((component for component in json_data if component['id'] == component_id), None) | |
| 143 | + return render_template('component-info.html', selected_component=selected_component) | |
| 144 | + | |
| 145 | + | |
| 146 | +@components_bp.route('/install_components_git/<string:component_id>') | |
| 147 | +def install_components_git(component_id): | |
| 148 | + # Шлях до головної папки проекту | |
| 149 | + project_folder = 'components' | |
| 150 | + # component = Component.get_repository_by_id(component_id) | |
| 151 | + response = requests.get(f'{k2.update_domain}api/components') | |
| 152 | + json_data = response.json() | |
| 153 | + selected_component = next((component for component in json_data if component['id'] == component_id), None) | |
| 154 | + # Назва репозиторія | |
| 155 | + repository_name = selected_component['name'] | |
| 156 | + # Шлях до папки репозиторія в межах проекту | |
| 157 | + repository_folder = os.path.join(project_folder, repository_name) | |
| 158 | + # URL репозиторія | |
| 159 | + | |
| 160 | + git_repo_url = selected_component['git_link'] | |
| 161 | + try: | |
| 162 | + # Перевірка наявності папки репозиторія | |
| 163 | + if not os.path.exists(repository_folder): | |
| 164 | + # Створення папки репозиторія | |
| 165 | + os.makedirs(repository_folder) | |
| 166 | + # Шлях до файлу __init__.py | |
| 167 | + init_file = os.path.join(repository_folder, '__init__.py') | |
| 168 | + # Перевірка наявності файлу __init__.py | |
| 169 | + if not os.path.exists(init_file): | |
| 170 | + # Створення пустого файлу __init__.py | |
| 171 | + open(init_file, 'a').close() | |
| 172 | + # підключення до приватного репозиторію | |
| 173 | + # os.environ['GITLAB_PRIVATE_TOKEN'] = '8xxTpxrKVDGoXD5ynjiW' | |
| 174 | + # git_repo_url_with_token = git_repo_url.replace('https://', | |
| 175 | + # f'https://oauth2:{os.environ["GITLAB_PRIVATE_TOKEN"]}@') | |
| 176 | + | |
| 177 | + # Команда для встановлення з використанням git_repo_url і повного шляху до папки репозиторія | |
| 178 | + command = ['venv/Scripts/python.exe', '-m', 'pip', 'install', '--use-pep517', 'git+' + git_repo_url, | |
| 179 | + '--target=' + repository_folder] | |
| 180 | + # Виконуємо команду встановлення | |
| 181 | + subprocess.check_call(command) | |
| 182 | + | |
| 183 | + # Шлях до файлу, до якого потрібно додати код | |
| 184 | + file_path = 'routes.py' | |
| 185 | + # Код, який потрібно додати | |
| 186 | + code = selected_component['dependencies'] | |
| 187 | + # Відкриття файлу у режимі дозапису | |
| 188 | + with open(file_path, 'a') as file: | |
| 189 | + # Запис нового коду у файл | |
| 190 | + file.write('\n') | |
| 191 | + file.write(code) | |
| 192 | + file.write('\n') | |
| 193 | + component = Component.query.filter_by(name=selected_component['name']).first() | |
| 194 | + if not component: | |
| 195 | + new_component = Component( | |
| 196 | + name=selected_component['name'], | |
| 197 | + description=selected_component['description'], | |
| 198 | + version=selected_component['version'], | |
| 199 | + git_link=selected_component['git_link'], | |
| 200 | + dependencies=selected_component['dependencies'], | |
| 201 | + installed=True | |
| 202 | + ) | |
| 203 | + # Add the new component to the database | |
| 204 | + db.session.add(new_component) | |
| 205 | + db.session.commit() | |
| 206 | + | |
| 207 | + # Повертаємо повідомлення про успішне встановлення | |
| 208 | + return f'Installed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />' | |
| 209 | + except subprocess.CalledProcessError as e: | |
| 210 | + return 'Error installing : ' + str(e) | |
| 211 | + | |
| 212 | + | |
| 213 | +@components_bp.route('/install_components/<string:component_id>') | |
| 214 | +def install_component_from_archive(component_id): | |
| 215 | + # Шлях до головної папки проекту | |
| 216 | + project_folder = 'components' | |
| 217 | + # Отримати відповідну компоненту зі списку компонент | |
| 218 | + response = requests.get(f'{k2.update_domain}api/components') | |
| 219 | + json_data = response.json() | |
| 220 | + selected_component = next((component for component in json_data if component['id'] == component_id), None) | |
| 221 | + if selected_component is None: | |
| 222 | + return 'Component not found' | |
| 223 | + # Отримати посилання на архів компоненти та версію | |
| 224 | + archive_url = selected_component['latest_component_data'] | |
| 225 | + version = selected_component['latest_version'] | |
| 226 | + try: | |
| 227 | + # Створити шлях до папки компоненти згідно назви та версії | |
| 228 | + component_folder = os.path.join(project_folder) | |
| 229 | + os.makedirs(component_folder, exist_ok=True) | |
| 230 | + # Завантажити архів компоненти | |
| 231 | + response = requests.get(archive_url, stream=True) | |
| 232 | + response.raise_for_status() | |
| 233 | + # Шлях до завантаженого архіву | |
| 234 | + archive_path = os.path.join(component_folder, f"{selected_component['name']}.zip") | |
| 235 | + # Встановити залежності з файлу requirements.txt | |
| 236 | + | |
| 237 | + # Зберегти архів на диск | |
| 238 | + with open(archive_path, "wb") as file: | |
| 239 | + for chunk in response.iter_content(chunk_size=8192): | |
| 240 | + file.write(chunk) | |
| 241 | + # Розпакувати архів | |
| 242 | + with zipfile.ZipFile(archive_path, "r") as zip_ref: | |
| 243 | + zip_ref.extractall(component_folder) | |
| 244 | + | |
| 245 | + # Видалити архів | |
| 246 | + os.remove(archive_path) | |
| 247 | + | |
| 248 | + # Шлях до файлу, до якого потрібно додати код | |
| 249 | + file_path = 'routes.py' | |
| 250 | + # Код, який потрібно додати | |
| 251 | + 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']}')''' | |
| 252 | + # selected_component['dependencies'] | |
| 253 | + | |
| 254 | + # Відкриття файлу у режимі дозапису | |
| 255 | + with open(file_path, 'a') as file: | |
| 256 | + # Запис нового коду у файл | |
| 257 | + file.write('\n') | |
| 258 | + file.write(code) | |
| 259 | + file.write('\n') | |
| 260 | + | |
| 261 | + # Update database with the installed component | |
| 262 | + component = Component.query.filter_by(name=selected_component['name']).first() | |
| 263 | + if not component: | |
| 264 | + component = Component( | |
| 265 | + name=selected_component['name'], | |
| 266 | + description=selected_component['description'], | |
| 267 | + version=version, | |
| 268 | + git_link=selected_component['git_link'], | |
| 269 | + dependencies=code, | |
| 270 | + installed=True | |
| 271 | + ) | |
| 272 | + db.session.add(component) | |
| 273 | + else: | |
| 274 | + component.version = version | |
| 275 | + component.git_link = selected_component['git_link'] | |
| 276 | + component.dependencies = code | |
| 277 | + component.installed = True | |
| 278 | + | |
| 279 | + db.session.commit() | |
| 280 | + | |
| 281 | + # add language folders | |
| 282 | + k2.search_babel_translation_directories() | |
| 283 | + return f'''Component installed successfully: {selected_component["name"]} v{version} {k2.babel_translation_directories}, | |
| 284 | + \n \n please wait installing requirments... | |
| 285 | + <meta http-equiv="refresh" content="0;url=/install-requirments/{selected_component["name"]}" />''' | |
| 286 | + | |
| 287 | + except Exception as e: | |
| 288 | + return f'Error installing component: {str(e)}' | |
| 289 | + | |
| 290 | + | |
| 291 | +@components_bp.route('/install-requirments/<string:selected_component_name>', methods=['GET']) | |
| 292 | +def install_requirments(selected_component_name): | |
| 293 | + # menu items | |
| 294 | + get_admin_menu() | |
| 295 | + | |
| 296 | + # install requirements | |
| 297 | + requirements_file = os.path.join('components', selected_component_name, "requirements.txt") | |
| 298 | + if os.path.isfile(requirements_file): | |
| 299 | + pip_command = f"{k2.venv_bin_path}/python -m pip install -r {requirements_file}" | |
| 300 | + subprocess.run(pip_command, shell=True, check=True) | |
| 301 | + return f'''Requirements installed successfully | |
| 302 | + \n \n please wait installing requirments components... | |
| 303 | + <meta http-equiv="refresh" content="1;url=/install-requirments-components" />''' | |
| 304 | + | |
| 305 | + | |
| 306 | +@components_bp.route('/install-requirments-components') | |
| 307 | +def install_requirements_components(): | |
| 308 | + # Откриття файлу requirements_components.txt | |
| 309 | + requirements_file = 'requirements_components.txt' | |
| 310 | + component_ids = None | |
| 311 | + if os.path.isfile(requirements_file): | |
| 312 | + with open('requirements_components.txt', 'r') as file: | |
| 313 | + component_ids = file.read().splitlines() | |
| 314 | + if component_ids: | |
| 315 | + for component_id in component_ids: | |
| 316 | + # Виклик роуту '/install_components/<string:component_name>' для кожної назви компоненти | |
| 317 | + response = requests.get( | |
| 318 | + f'/install_components/{component_id}') | |
| 319 | + return f'Requirements components installed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />' | |
| 320 | + else: | |
| 321 | + return f'<meta http-equiv="refresh" content="1;url=/dashboard" />' | |
| 322 | + # Опрацювання відповіді (за потреби) | |
| 323 | + | |
| 324 | + | |
| 325 | +@components_bp.route('/remove_dependencies/<string:component_id>', methods=['GET']) | |
| 326 | +def remove_dependencies(component_id): | |
| 327 | + # Знаходимо компоненту за її ID | |
| 328 | + component = Component.query.get(component_id) | |
| 329 | + if not component: | |
| 330 | + return 'Component not found <meta http-equiv="refresh" content="1;url=/dashboard" />' | |
| 331 | + # remove menu items | |
| 332 | + component_name = component.name | |
| 333 | + # file_path = "components/menu.yml" | |
| 334 | + # remove_from_yaml_file(file_path, component_name) | |
| 335 | + requests.get(f"{k2.domain}api/add-to-menu") | |
| 336 | + # remove routes | |
| 337 | + file_path = 'routes.py' | |
| 338 | + with open(file_path, 'r') as file: | |
| 339 | + lines = file.readlines() | |
| 340 | + | |
| 341 | + updated_lines = [line for line in lines if line.strip() not in component.dependencies] | |
| 342 | + with open(file_path, 'w') as file: | |
| 343 | + file.writelines(updated_lines) | |
| 344 | + | |
| 345 | + component.installed = False | |
| 346 | + db.session.commit() | |
| 347 | + | |
| 348 | + return 'Component successfully turn off <meta http-equiv="refresh" content="1;url=/dashboard" />' | |
| 349 | + | |
| 350 | + | |
| 351 | +@components_bp.route('/add_dependencies/<string:component_id>', methods=['GET']) | |
| 352 | +def add_dependencies(component_id): | |
| 353 | + # Знаходимо компоненту за її ID | |
| 354 | + component = Component.query.get(component_id) | |
| 355 | + if not component: | |
| 356 | + return 'Component not found <meta http-equiv="refresh" content="1;url=/dashboard" />' | |
| 357 | + | |
| 358 | + file_path = 'routes.py' | |
| 359 | + code = component.dependencies | |
| 360 | + # Відкриття файлу у режимі дозапису | |
| 361 | + with open(file_path, 'a') as file: | |
| 362 | + # Запис нового коду у файл | |
| 363 | + file.write(code) | |
| 364 | + | |
| 365 | + # Оновлюємо статус компоненти | |
| 366 | + component.installed = True | |
| 367 | + db.session.commit() | |
| 368 | + return 'Dependencies added successfully <meta http-equiv="refresh" content="1;url=/dashboard" />' | |
| 369 | + | |
| 370 | + | |
| 371 | +@components_bp.route('/remove-component/<string:component_id>') | |
| 372 | +def remove_component(component_id): | |
| 373 | + # Шлях до головної папки проекту | |
| 374 | + project_folder = 'components' | |
| 375 | + component = Component.query.get(component_id) | |
| 376 | + # Назва репозиторія | |
| 377 | + repository_name = component.name | |
| 378 | + # Шлях до папки репозиторія в межах проекту | |
| 379 | + repository_folder = os.path.join(project_folder, repository_name) | |
| 380 | + try: | |
| 381 | + # Перевірка наявності папки репозиторія | |
| 382 | + if os.path.exists(repository_folder): | |
| 383 | + # Видалення папки репозиторія | |
| 384 | + shutil.rmtree(repository_folder) | |
| 385 | + # Видаляємо компоненту з бази | |
| 386 | + if component: | |
| 387 | + db.session.delete(component) | |
| 388 | + db.session.commit() | |
| 389 | + | |
| 390 | + # Повертаємо повідомлення про успішне видалення | |
| 391 | + return 'Component removed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />' | |
| 392 | + except Exception as e: | |
| 393 | + # Повертаємо повідомлення про помилку видалення | |
| 394 | + return 'Error removing component: ' + str(e) | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | +@components_bp.route('/component/add') | |
| 400 | +def component_add(): | |
| 401 | + # components for install | |
| 402 | + try: | |
| 403 | + # GET-requests to API | |
| 404 | + response = requests.get(f'{k2.update_domain}api/components') | |
| 405 | + json_data = response.json() | |
| 406 | + for item in json_data: | |
| 407 | + item['button'] = f"{k2.domain}/install_components/{item['id']}" | |
| 408 | + | |
| 409 | + except: | |
| 410 | + json_data = None | |
| 411 | + return jsonify(json_data) | |
| 412 | + | |
| 413 | + | |
| 414 | +@components_bp.route('/component/list') | |
| 415 | +def component_list(): | |
| 416 | + # Встановлені компоненти | |
| 417 | + component_list = [] | |
| 418 | + components = Component.query.all() | |
| 419 | + for component in components: | |
| 420 | + component_dict = {} | |
| 421 | + component_dict['name'] = component.name | |
| 422 | + component_dict['id'] = component.id | |
| 423 | + component_dict['description'] = component.description | |
| 424 | + component_dict['version'] = component.version | |
| 425 | + component_dict['button_off'] =f"{k2.domain}/remove_dependencies/{component.id}" | |
| 426 | + component_dict['button_on'] = f"{k2.domain}/add_dependencies/{component.id}" | |
| 427 | + component_dict['button_del'] = f"{k2.domain}/remove-component/{component.id}" | |
| 428 | + component_list.append(component_dict) | |
| 429 | + return jsonify(component_list) | |
| 430 | + | |
| 431 | +# menu | |
| 432 | +@components_bp.route('/api/add-to-menu', methods=['GET']) | |
| 433 | +def add_to_menu(): | |
| 434 | + data = [] | |
| 435 | + response = requests.get(f"{k2.domain}/menu-admin-items") | |
| 436 | + data.append(response.json()[0]) | |
| 437 | + components = Component.query.all() | |
| 438 | + components_names = [component.name for component in components] | |
| 439 | + for components_names in components_names: | |
| 440 | + response = requests.get(f"{k2.domain}{components_names}/menu-admin-items") | |
| 441 | + print(response) | |
| 442 | + if response.status_code == 200: | |
| 443 | + data.append(response.json()[0]) | |
| 444 | + else: | |
| 445 | + data | |
| 446 | + # add component_name key for menu items | |
| 447 | + for item in data: | |
| 448 | + item['component_name'] = components_names | |
| 449 | + k2.menu = data | |
| 450 | + return k2.menu | |
| 451 | + | |
| 452 | + | |
| 453 | +@components_bp.route('/api/main-menu/') | |
| 454 | +def get_admin_menu(): | |
| 455 | + requests.get(f"{k2.domain}api/add-to-menu") | |
| 456 | + return k2.menu | |
| 457 | + | |
| 458 | + | |
| 459 | +@components_bp.route('/menu-admin-items') | |
| 460 | +def menu_items(): | |
| 461 | + menu = [ | |
| 462 | + { | |
| 463 | + "title": 'Add components', | |
| 464 | + "icon": {"icon": 'mdi-account-circle-outline'}, | |
| 465 | + "children": [ | |
| 466 | + {'title': 'Install components', 'to': 'component-add'}, | |
| 467 | + {'title': 'Installed components', 'to': 'component-list'}, | |
| 468 | + | |
| 469 | + ], | |
| 470 | + }] | |
| 471 | + return menu | |
| 472 | + |
languages/babel_translation_directories.yml
main.py
| ... | ... | @@ -8,7 +8,7 @@ from k2.k2cfg import k2 |
| 8 | 8 | # configure flask app |
| 9 | 9 | app = Flask(__name__) |
| 10 | 10 | app.config['SECRET_KEY'] = k2.secret_key |
| 11 | - | |
| 11 | +app.json.sort_keys = k2.json_sort_keys | |
| 12 | 12 | #app.config.from_object(k2) |
| 13 | 13 | |
| 14 | 14 | # init db |
| ... | ... | @@ -22,9 +22,6 @@ app.config['BABEL_TRANSLATION_DIRECTORIES'] = k2.babel_translation_directories |
| 22 | 22 | babel = Babel(app) |
| 23 | 23 | babel.init_app(app, locale_selector=k2.get_locale) |
| 24 | 24 | |
| 25 | -#menu | |
| 26 | -k2.get_menu_elenents() | |
| 27 | - | |
| 28 | 25 | # flask routing |
| 29 | 26 | from routes import * |
| 30 | 27 |
requirements.txt
routes.py
| 1 | -from flask import Flask | |
| 2 | 1 | #app = Flask(__name__) |
| 3 | -from main import app, babel | |
| 4 | -from k2.k2inst import components_bp | |
| 2 | +from main import app | |
| 3 | +from k2.k2rout import components_bp | |
| 5 | 4 | app.register_blueprint(components_bp) |
| 6 | - | |
| 7 | 5 | from components.k2test.k2test.views import k2test |
| 8 | 6 | app.register_blueprint(k2test, url_prefix='/k2test') |
| 9 | - | |
| 10 | -from components.adm.adm.views import adm | |
| 11 | -app.register_blueprint(adm, url_prefix='/adm') | |
| 7 | +from components.grid.grid.views import grid | |
| 8 | +app.register_blueprint(grid, url_prefix='/grid') | |
| 9 | +from components.vue.vue.views import vue2 | |
| 10 | +app.register_blueprint(vue2, url_prefix='/') |
templates/dashboard.html
| ... | ... | @@ -112,7 +112,7 @@ |
| 112 | 112 | |
| 113 | 113 | |
| 114 | 114 | <div class="container px-4 py-5" id="hanging-icons"> |
| 115 | - <h2 class="pb-2 border-bottom">{{_('Components Shop')}}</h2> | |
| 115 | + <h2 class="pb-2 border-bottom">{{_('ADD Components')}}</h2> | |
| 116 | 116 | <form action="{{ url_for('components.dashboard') }}" method="GET" class="search-form"> |
| 117 | 117 | <div class="input-group"> |
| 118 | 118 | <input type="text" name="search_query" class="form-control" placeholder="Search components"> |
templates/first-login.html
| ... | ... | @@ -64,7 +64,7 @@ |
| 64 | 64 | <img class="logo" src="{{ url_for('static', filename='img/logo-k2.png')}}" alt="" width="250"> |
| 65 | 65 | <h1>K2CloudERP-2.5.1.27</h1> |
| 66 | 66 | |
| 67 | - <form method="post" action="/login"> | |
| 67 | + <form method="post" action="/first-login"> | |
| 68 | 68 | <div class="form-group"> |
| 69 | 69 | <p><input type="text" name="username" placeholder="Ім'я користувача"></p> |
| 70 | 70 | </div> |