Compare View

switch
from
...
to
 
Commits (5)

Changes

Showing 56 changed files Inline Diff

File was created 1 /components/adm/
2 /components/grid/
3 /components/vue/
1 /components/adm/ 4
__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
File was created 1 1.0.0.0 base component
2 1.0.0.1 add some routes
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 "New 2 page"
2 msgstr "Нова сторінка 2"
3 msgid "title_menu_items"
4 msgstr "Test menu items"
components/k2test/k2test/languages/uk/LC_MESSAGES/messages.mo
No preview for this file type
components/k2test/k2test/languages/uk/LC_MESSAGES/messages.po
1 msgid "New 2 page" 1 msgid "New 2 page"
2 msgstr "Нова сторінка 2" 2 msgstr "Нова сторінка 2"
3 msgid "title_menu_items"
4 msgstr "Тестова компонента"
components/k2test/k2test/views.py
1 from flask import render_template, redirect, request, jsonify 1 from flask import render_template, redirect, request, jsonify
2 from flask_babel import gettext
2 from flask_babel import gettext 3 from flask_login import login_user, current_user, logout_user, login_required
3 from flask_login import login_user, current_user, logout_user, login_required 4 from flask import current_app as k2
4 from flask import current_app as k2 5 from flask import Blueprint, json
5 from flask import Blueprint, json 6 from flask_sqlalchemy import SQLAlchemy
6 from flask_sqlalchemy import SQLAlchemy 7
7 8
9 #bliueprint
10 k2test = Blueprint('k2test', __name__, template_folder='templates', static_folder='static')
8 11
9 #bliueprint 12 #component routes
10 k2test = Blueprint('k2test', __name__, template_folder='templates', static_folder='static')
11
12 #component routes
13 @k2test.route('/new') 13 @k2test.route('/new')
14 def new_component(): 14 def new_component():
15 db = k2.extensions['sqlalchemy'].db 15 db = k2.extensions['sqlalchemy'].db
16 connection = db.engine.connect() 16 connection = db.engine.connect()
17 query = db.text("SELECT email, login FROM k2users") 17 query = db.text("SELECT email, login FROM k2users")
18 result = connection.execute(query).fetchall() 18 result = connection.execute(query).fetchall()
19 rows = [dict(email=row.email, login=row.login) for row in result] 19 rows = [dict(email=row.email, login=row.login) for row in result]
20 # Перетворення результату на JSON 20 # Перетворення результату на JSON
21 json_result = json.dumps(rows) 21 json_result = json.dumps(rows)
22 return json_result 22 return json_result
23 23
24 @k2test.route('/new2') 24 @k2test.route('/new2')
25 def new_component2(): 25 def new_component2():
26 return render_template('new2.html') 26 return render_template('new2.html')
27 27
28 @k2test.route('/new3') 28 @k2test.route('/new3')
29 def new_component3(): 29 def new_component3():
30 return jsonify({'data': 'page new3'}) 30 return jsonify({'data': 'page new3'})
31 31
32 @k2test.route('/new4') 32 @k2test.route('/new4')
33 def new_component4(): 33 def new_component4():
34 return jsonify({'data': 'page new4'}) 34 return jsonify({'data': 'page new4'})
35 35
36 # menu items for admin 36 # menu items for admin
37 @k2test.route('/menu-admin-items') 37 @k2test.route('/menu-admin-items')
38 def menu_items(): 38 def menu_items():
39 data = [ 39 data = [
40 { 40 {
41 'title': f"{gettext('title_menu_items')}", 41 'title': f"{gettext('title_menu_items')}",
42 'icon': {'icon': 'mdi-chart-timeline-variant'}, 42 'icon': {'icon': 'mdi-chart-timeline-variant'},
43 'children': [ 43 'children': [
44 {'title': 'Тест 3', 'to': 'k2test-new3'}, 44 {'title': 'Тест 3', 'to': 'k2test-new3'},
45 {'title': 'Тест 3', 'to': 'k2test-new4'}, 45 {'title': 'Тест 3', 'to': 'k2test-new4'},
46 ], 46 ],
47 } 47 }
48 48
49 ] 49 ]
components/k2test/setup.py
1 from setuptools import setup, find_packages 1 from setuptools import setup, find_packages
2 2
3 setup( 3 setup(
4 name='k2test', 4 name='k2test',
5 version='1.0.0', 5 version='1.0.0',
6 description='Description of my project', 6 description='Description of my project',
7 author='k2 cloud', 7 author='k2 cloud',
8 author_email='rs@corp2.net', 8 author_email='rs@corp2.net',
9 keywords="k2test, k2", 9 keywords="k2test, k2",
10 python_requires=">=3.7, <=3.11.2", 10 python_requires=">=3.7, <=3.11.2",
11 packages=['k2test'], 11 packages=['k2test'],
12 package_data={ # Optional 12 package_data={ # Optional
13 }, 13 },
14 install_requires=[""], 14 install_requires=[""],
15 ) 15 )
16 16
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
1 from flask_sqlalchemy import SQLAlchemy 1 from flask_sqlalchemy import SQLAlchemy
2 from sqlalchemy import text 2 from sqlalchemy import text
3 from .k2obj import k2obj 3 from .k2obj import k2obj
4 import yaml 4 import yaml
5 import os 5 import os
6 import requests 6 import requests
7 import pytz 7 import pytz
8 import os 8 import os
9 import sys 9 import sys
10 from flask import session 10 from flask import session
11 11
12
13
12 14 class k2(k2obj):
13 15 version = '2.5.1.27'
14 class k2(k2obj): 16 db = SQLAlchemy()
15 version = '2.5.1.27' 17 domain = 'http://127.0.0.1:5000/'
16 db = SQLAlchemy() 18 update_domain ='http://127.0.0.1:8002/'
17 domain = 'http://127.0.0.1:5000/' 19 venv_bin_path = os.path.join(os.path.dirname(sys.prefix), 'venv/Scripts')
18 update_domain ='http://127.0.0.1:8002/' 20 json_sort_keys = False
19 venv_bin_path = os.path.join(os.path.dirname(sys.prefix), 'venv/Scripts') 21 timezone = pytz.timezone('Europe/Kiev')
20 json_sort_keys = False 22 secret_key = 'JH&INH987gFDHdsagh&8dwbjdw8ckw'
21 timezone = pytz.timezone('Europe/Kiev') 23 default_language = 'en'
22 secret_key = 'JH&INH987gFDHdsagh&8dwbjdw8ckw' 24 current_language = ''
23 default_language = 'en' 25 babel_translation_directories = ''
26 components = ''
27
28
24 current_language = '' 29
25 babel_translation_directories = '' 30 def __init__(self):
26 components = '' 31 super().__init__()
27 32
28 33 @classmethod
29 34 def load_babel_translation_directories(cls):
30 def __init__(self): 35 with open('languages/babel_translation_directories.yml', 'r') as f:
31 super().__init__() 36 data = yaml.safe_load(f)
32 37 cls.babel_translation_directories = data['babel_translation_directories']
33 @classmethod 38
34 def load_babel_translation_directories(cls): 39 @classmethod
35 with open('languages/babel_translation_directories.yml', 'r') as f: 40 def get_locale(cls):
36 data = yaml.safe_load(f) 41 if 'lang' in session:
37 cls.babel_translation_directories = data['babel_translation_directories'] 42 # Отримати обрану мову з сеансу користувача
38 43 return session['lang']
39 @classmethod 44 else:
40 def get_locale(cls): 45 # Повернути мову за замовчуванням з налаштувань додатку
41 if 'lang' in session: 46 return cls.default_language #app.config['DEFAULT_LANGUAGE']
42 # Отримати обрану мову з сеансу користувача 47
43 return session['lang'] 48 @classmethod
44 else: 49 def search_babel_translation_directories(cls):
45 # Повернути мову за замовчуванням з налаштувань додатку 50 response = requests.get(f'{cls.domain}api/languages')
46 return cls.default_language #app.config['DEFAULT_LANGUAGE'] 51 if response.status_code == 200:
47 52 cls.babel_translation_directories = response.text
48 @classmethod 53 data = {'babel_translation_directories': response.text}
49 def search_babel_translation_directories(cls): 54 else:
50 response = requests.get(f'{cls.domain}api/languages') 55 cls.babel_translation_directories = 'languages'
51 if response.status_code == 200: 56 data = {'babel_translation_directories': 'languages'}
52 cls.babel_translation_directories = response.text 57
53 data = {'babel_translation_directories': response.text} 58 with open('languages/babel_translation_directories.yml', 'w') as f:
54 else: 59 yaml.dump(data, f)
55 cls.babel_translation_directories = 'languages'
56 data = {'babel_translation_directories': 'languages'}
57
58 with open('languages/babel_translation_directories.yml', 'w') as f:
59 yaml.dump(data, f)
60
61 # load database settings from yml file 60
62 def init_db_uri(self): 61 # load database settings from yml file
63 with open('db/db.yml', 'r') as f: 62 def init_db_uri(self):
64 db_config = yaml.load(f, Loader=yaml.FullLoader) 63 with open('db/db.yml', 'r') as f:
65 return db_config 64 db_config = yaml.load(f, Loader=yaml.FullLoader)
66 65 return db_config
67 # creating a connection 66
68 def init_db(self, app): 67 # creating a connection
69 db_config = self.init_db_uri() 68 def init_db(self, app):
70 db_uri = f"mysql+{db_config['driver']}://{db_config['user']}:{db_config['password']}@{db_config['host']}/{db_config['dbname']}" 69 db_config = self.init_db_uri()
71 app.config['SQLALCHEMY_DATABASE_URI'] = db_uri 70 db_uri = f"mysql+{db_config['driver']}://{db_config['user']}:{db_config['password']}@{db_config['host']}/{db_config['dbname']}"
File was created 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
1 import uuid 41
1 class k2obj: 1 class k2obj:
2 name = '' 2 name = ''
3 version = '' 3 version = ''
4 domain = '' 4 domain = ''
5 timezone = '' 5 timezone = ''
6 menu = '' 6 menu = ''
7 permissions = { 7 permissions = {
8 'r': '0', 8 'r': '0',
9 'w': '0', 9 'w': '0',
10 'i': '0', 10 'i': '0',
11 'd': '0', 11 'd': '0',
12 'c': '0', 12 'c': '0',
13 'exp': '0', 13 'exp': '0',
14 'imp': '0', 14 'imp': '0',
15 'settable': '0', 15 'settable': '0',
16 'cutpast': '0', 16 'cutpast': '0',
17 'enable': '0' 17 'enable': '0'
18 } 18 }
19 components = ''
19 components = '' 20
20 21
21 22
22 23
23 24
24 25
File was created 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
1 import zipfile 473
languages/babel_translation_directories.yml
1 babel_translation_directories: languages;components/k2test/k2test/languages;components/grid/grid/languages;components/adm/adm/languages 1 babel_translation_directories: languages;components/k2test/k2test/languages;components/grid/grid/languages;components/adm/adm/languages
2 2
1 from flask_cors import CORS 1 from flask_cors import CORS
2 from flask_babel import Babel 2 from flask_babel import Babel
3 from flask_jwt_extended import JWTManager 3 from flask_jwt_extended import JWTManager
4 #from k2.k2inst import * 4 #from k2.k2inst import *
5 from flask import Flask 5 from flask import Flask
6 from k2.k2cfg import k2 6 from k2.k2cfg import k2
7 7
8 # configure flask app 8 # configure flask app
9 app = Flask(__name__) 9 app = Flask(__name__)
10 app.config['SECRET_KEY'] = k2.secret_key 10 app.config['SECRET_KEY'] = k2.secret_key
11 app.json.sort_keys = k2.json_sort_keys 11 app.json.sort_keys = k2.json_sort_keys
12 #app.config.from_object(k2) 12 #app.config.from_object(k2)
13 13
14 # init db 14 # init db
15 k2().init_db(app) 15 k2().init_db(app)
16 CORS(app) 16 CORS(app)
17 jwt = JWTManager(app) 17 jwt = JWTManager(app)
18 18
19 #translate 19 #translate
20 k2.load_babel_translation_directories() 20 k2.load_babel_translation_directories()
21 app.config['BABEL_TRANSLATION_DIRECTORIES'] = k2.babel_translation_directories 21 app.config['BABEL_TRANSLATION_DIRECTORIES'] = k2.babel_translation_directories
22 babel = Babel(app) 22 babel = Babel(app)
23 babel.init_app(app, locale_selector=k2.get_locale) 23 babel.init_app(app, locale_selector=k2.get_locale)
24 24
25 # flask routing
26 from routes import *
27
28 if __name__ == '__main__': 25 # flask routing
29 app.run(debug=True, port=5000) 26 from routes import *
30 27
31 28 if __name__ == '__main__':
32 29 app.run(debug=True, port=5000)
33 30
34 31
35 32
1 alembic==1.11.1 1 alembic==1.11.1
2 Babel==2.12.1 2 Babel==2.12.1
3 bcrypt==4.0.1 3 bcrypt==4.0.1
4 bidict==0.22.1 4 bidict==0.22.1
5 blinker==1.6.2 5 blinker==1.6.2
6 certifi==2023.5.7 6 certifi==2023.5.7
7 charset-normalizer==3.1.0 7 charset-normalizer==3.1.0
8 click==8.1.3 8 click==8.1.3
9 colorama==0.4.6 9 colorama==0.4.6
10 dnspython==2.3.0 10 dnspython==2.3.0
11 email-validator==2.0.0.post2 11 email-validator==2.0.0.post2
12 Flask==2.3.2 12 Flask==2.3.2
13 flask-babel==3.1.0 13 flask-babel==3.1.0
14 Flask-Bcrypt==1.0.1 14 Flask-Bcrypt==1.0.1
15 Flask-Cors==3.0.10 15 Flask-Cors==3.0.10
16 Flask-JWT-Extended==4.5.2 16 Flask-JWT-Extended==4.5.2
17 Flask-Login==0.6.2 17 Flask-Login==0.6.2
18 Flask-Migrate==4.0.4 18 Flask-Migrate==4.0.4
19 Flask-MySQLdb==1.0.1 19 Flask-MySQLdb==1.0.1
20 Flask-SQLAlchemy==3.0.3 20 Flask-SQLAlchemy==3.0.3
21 flask-staticdirs==1.0.1 21 flask-staticdirs==1.0.1
22 Flask-WTF==1.1.1 22 Flask-WTF==1.1.1
23 greenlet==2.0.2 23 greenlet==2.0.2
24 idna==3.4 24 idna==3.4
25 itsdangerous==2.1.2 25 itsdangerous==2.1.2
26 Jinja2==3.1.2 26 Jinja2==3.1.2
27 jsonify==0.5 27 jsonify==0.5
28 Mako==1.2.4 28 Mako==1.2.4
29 MarkupSafe==2.1.2 29 MarkupSafe==2.1.2
30 mysql==0.0.3 30 mysql==0.0.3
31 mysql-connector==2.2.9 31 mysql-connector==2.2.9
32 mysql-connector-python==8.0.33 32 mysql-connector-python==8.0.33
33 mysqlclient==2.1.1 33 mysqlclient==2.1.1
34 peppercorn==0.6 34 peppercorn==0.6
35 protobuf==3.20.3 35 protobuf==3.20.3
36 PyJWT==2.7.0 36 PyJWT==2.7.0
37 python-engineio==4.4.1 37 python-engineio==4.4.1
38 python-socketio==5.8.0 38 python-socketio==5.8.0
39 pytz==2023.3 39 pytz==2023.3
40 PyYAML==6.0 40 PyYAML==6.0
41 requests==2.31.0 41 requests==2.31.0
42 six==1.16.0 42 six==1.16.0
43 SQLAlchemy==2.0.15 43 SQLAlchemy==2.0.15
44 typing_extensions==4.6.2 44 typing_extensions==4.6.2
45 urllib3==2.0.2 45 urllib3==2.0.2
46 Werkzeug==2.3.4 46 Werkzeug==2.3.4
47 WTForms==3.0.1 47 WTForms==3.0.1
48 48
1 #app = Flask(__name__)
2 from main import app 1 #app = Flask(__name__)
3 from k2.k2rout import components_bp 2 from main import app
4 app.register_blueprint(components_bp) 3 from k2.k2rout import components_bp
5 from components.k2test.k2test.views import k2test 4 app.register_blueprint(components_bp)
6 app.register_blueprint(k2test, url_prefix='/k2test')
7 from components.grid.grid.views import grid 5 from components.k2test.k2test.views import k2test
8 app.register_blueprint(grid, url_prefix='/grid') 6 app.register_blueprint(k2test, url_prefix='/k2test')
9 from components.vue.vue.views import vue2 7 from components.grid.grid.views import grid
10 app.register_blueprint(vue2, url_prefix='/') 8 app.register_blueprint(grid, url_prefix='/grid')
11 9 from components.vue.vue.views import vue2
10 app.register_blueprint(vue2, url_prefix='/')
12 from components.adm.adm.views import adm 11
13 app.register_blueprint(adm, url_prefix='/adm')
templates/dashboard.html
1 {% extends "base.html" %} 1 {% extends "base.html" %}
2 2
3 {% block styles %} 3 {% block styles %}
4 <style> 4 <style>
5 .table { 5 .table {
6 border-collapse: collapse; 6 border-collapse: collapse;
7 width: 100%; 7 width: 100%;
8 } 8 }
9 9
10 .table th, .table td { 10 .table th, .table td {
11 text-align: left; 11 text-align: left;
12 padding: 8px; 12 padding: 8px;
13 border-bottom: 1px solid #ddd; 13 border-bottom: 1px solid #ddd;
14 } 14 }
15 15
16 .table tr:hover { 16 .table tr:hover {
17 background-color: #f5f5f5; 17 background-color: #f5f5f5;
18 } 18 }
19 19
20 .search-form { 20 .search-form {
21 margin-bottom: 20px; 21 margin-bottom: 20px;
22 } 22 }
23 23
24 .search-form .input-group { 24 .search-form .input-group {
25 width: 300px; 25 width: 300px;
26 } 26 }
27 </style> 27 </style>
28 {% endblock %} 28 {% endblock %}
29 29
30 {% block content %} 30 {% block content %}
31 <h1>K2CloudERP-2.5.1.27</h1> 31 <h1>K2CloudERP-2.5.1.27</h1>
32 <p> <a href="/logout">вихід</a></p> 32 <p> <a href="/logout">вихід</a></p>
33 <p> <a href="{{ url_for('components.change_language', lang='en') }}">en</a> | <a href="{{ url_for('components.change_language', lang='uk') }}">uk</a></p> 33 <p> <a href="{{ url_for('components.change_language', lang='en') }}">en</a> | <a href="{{ url_for('components.change_language', lang='uk') }}">uk</a></p>
34 {{language}} 34 {{language}}
35 {{lang_d}} 35 {{lang_d}}
36 36
37 <h2>{{ _('Installed components') }}</h2> 37 <h2>{{ _('Installed components') }}</h2>
38 38
39 <table class="table"> 39 <table class="table">
40 <thead> 40 <thead>
41 <tr> 41 <tr>
42 <th>{{_('Name')}}</th> 42 <th>{{_('Name')}}</th>
43 <th>{{_('Description')}}</th> 43 <th>{{_('Description')}}</th>
44 <th>{{_('Version')}}</th> 44 <th>{{_('Version')}}</th>
45 <th>{{_('Dependencies')}}</th> 45 <th>{{_('Dependencies')}}</th>
46 <th>{{_('Git Link')}}</th> 46 <th>{{_('Git Link')}}</th>
47 <th>{{_('Install At')}}</th> 47 <th>{{_('Install At')}}</th>
48 <th>{{_('Status')}}</th> 48 <th>{{_('Status')}}</th>
49 <th>{{_('Action')}}</th> 49 <th>{{_('Action')}}</th>
50 </tr> 50 </tr>
51 </thead> 51 </thead>
52 52
53 <tbody> 53 <tbody>
54 <tr> 54 <tr>
55 <td>k2root</td> 55 <td>k2root</td>
56 <td>main component</td> 56 <td>main component</td>
57 <td colspan="6">2.5.1.27</td></tr> 57 <td colspan="6">2.5.1.27</td></tr>
58 58
59 {% for component in components %} 59 {% for component in components %}
60 <tr> 60 <tr>
61 <td>{{ component.name }}</td> 61 <td>{{ component.name }}</td>
62 <td>{{ component.description }}</td> 62 <td>{{ component.description }}</td>
63 <td>{{ component.version }}</td> 63 <td>{{ component.version }}</td>
64 <td>{{ component.dependencies }}</td> 64 <td>{{ component.dependencies }}</td>
65 <td>{{ component.git_link }}</td> 65 <td>{{ component.git_link }}</td>
66 <td>{{ component.date_installed }}</td> 66 <td>{{ component.date_installed }}</td>
67 <td> 67 <td>
68 {% if component.installed %} 68 {% if component.installed %}
69 Yes 69 Yes
70 {% else %} 70 {% else %}
71 No 71 No
72 {% endif %} 72 {% endif %}
73 </td> 73 </td>
74 <td> 74 <td>
75 {% if component.installed %} 75 {% if component.installed %}
76 <a href="/remove_dependencies/{{ component.id }}" class="btn btn-primary">Вимкнути</a> 76 <a href="/remove_dependencies/{{ component.id }}" class="btn btn-primary">Вимкнути</a>
77 {% else %} 77 {% else %}
78 <a href="/add_dependencies/{{ component.id }}" class="btn btn-primary">Увімкнути</a> 78 <a href="/add_dependencies/{{ component.id }}" class="btn btn-primary">Увімкнути</a>
79 <!-- Button trigger modal --> 79 <!-- Button trigger modal -->
80 <button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#components-{{ component.id }}"> 80 <button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#components-{{ component.id }}">
81 Видалити 81 Видалити
82 </button> 82 </button>
83 83
84 <!-- Modal --> 84 <!-- Modal -->
85 <div class="modal fade" id="components-{{ component.id }}" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true"> 85 <div class="modal fade" id="components-{{ component.id }}" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
86 <div class="modal-dialog"> 86 <div class="modal-dialog">
87 <div class="modal-content"> 87 <div class="modal-content">
88 <div class="modal-header"> 88 <div class="modal-header">
89 <h1 class="modal-title fs-5" id="exampleModalLabel">Modal title</h1> 89 <h1 class="modal-title fs-5" id="exampleModalLabel">Modal title</h1>
90 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 90 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
91 </div> 91 </div>
92 <div class="modal-body"> 92 <div class="modal-body">
93 Видалити компоненту? 93 Видалити компоненту?
94 </div> 94 </div>
95 <div class="modal-footer"> 95 <div class="modal-footer">
96 <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> 96 <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
97 <a href="/remove-component/{{ component.id }}" class="btn btn-danger">Видалити</a> 97 <a href="/remove-component/{{ component.id }}" class="btn btn-danger">Видалити</a>
98 </div> 98 </div>
99 </div> 99 </div>
100 </div> 100 </div>
101 </div> 101 </div>
102 {% endif %} 102 {% endif %}
103 </td> 103 </td>
104 </tr> 104 </tr>
105 {% endfor %} 105 {% endfor %}
106 </tbody> 106 </tbody>
107 </table> 107 </table>
108 108
109 109
110 110
111 111
112 112
113 113
114 <div class="container px-4 py-5" id="hanging-icons"> 114 <div class="container px-4 py-5" id="hanging-icons">
115 <h2 class="pb-2 border-bottom">{{_('ADD Components')}}</h2> 115 <h2 class="pb-2 border-bottom">{{_('ADD Components')}}</h2>
116 <form action="{{ url_for('components.dashboard') }}" method="GET" class="search-form"> 116 <form action="{{ url_for('components.dashboard') }}" method="GET" class="search-form">
117 <div class="input-group"> 117 <div class="input-group">
118 <input type="text" name="search_query" class="form-control" placeholder="Search components"> 118 <input type="text" name="search_query" class="form-control" placeholder="Search components">
119 <button type="submit" class="btn btn-primary">Search</button> 119 <button type="submit" class="btn btn-primary">Search</button>
120 </div> 120 </div>
121 </form> 121 </form>
122 122
123 123
124 <div class="row g-4 py-5 row-cols-1 row-cols-lg-3"> 124 <div class="row g-4 py-5 row-cols-1 row-cols-lg-3">
125 {% if component_server %} 125 {% if component_server %}
126 {% for component in component_server %} 126 {% for component in component_server %}
127 <div class="col d-flex align-items-start"> 127 <div class="col d-flex align-items-start">
128 <div class="icon-square bg-light text-dark flex-shrink-0 me-3"> 128 <div class="icon-square bg-light text-dark flex-shrink-0 me-3">
129 <img class="d-block mx-auto mb-4" src="/static/img/logo-k2.png" alt="" width="60"> 129 <img class="d-block mx-auto mb-4" src="/static/img/logo-k2.png" alt="" width="60">
130 </div> 130 </div>
131 <div> 131 <div>
132 <h2>{{ component.name|title }}</h2> 132 <h2>{{ component.name|title }}</h2>
133 <p>{{ component.description }}</p> 133 <p>{{ component.description }}</p>
134 <p>ВЕРСІЯ {{ component.latest_version }}</p> 134 <p>ВЕРСІЯ {{ component.latest_version }}</p>
135 {% if component.name in components_names %} 135 {% if component.name in components_names %}
136 <button type="button" class="btn btn-success">Installed</button> 136 <button type="button" class="btn btn-success">Installed</button>
137 {% else %} 137 {% else %}
138 <a href="/show_components/{{ component.id }}" class="btn btn-primary">Install</a> 138 <a href="/show_components/{{ component.id }}" class="btn btn-primary">Install</a>
139 {% endif %} 139 {% endif %}
140 </div> 140 </div>
141 </div> 141 </div>
142 {% endfor %} 142 {% endfor %}
143 {% else %} 143 {% else %}
144 <div class="col"> 144 <div class="col">
145 <p>No components available.</p> 145 <p>No components available.</p>
146 </div> 146 </div>
147 {% endif %} 147 {% endif %}
148 </div> 148 </div>
149 </div> 149 </div>
150 150
151 {% endblock %} 151 {% endblock %}
152 152
templates/first-login.html
1 <!DOCTYPE html> 1 <!DOCTYPE html>
2 <html> 2 <html>
3 <head> 3 <head>
4 4
5 5
6 <title>K2CloudERP-2.5.1.27 Login</title> 6 <title>K2CloudERP-2.5.1.27 Login</title>
7 <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}"> 7 <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
8 <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap.min.css') }}"> 8 <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
9 9
10 <style> 10 <style>
11 body { 11 body {
12 display: flex; 12 display: flex;
13 justify-content: center; 13 justify-content: center;
14 align-items: center; 14 align-items: center;
15 height: 100vh; 15 height: 100vh;
16 } 16 }
17 17
18 .login-form { 18 .login-form {
19 max-width: 600px; 19 max-width: 600px;
20 padding: 20px; 20 padding: 20px;
21 border: 1px solid #ddd; 21 border: 1px solid #ddd;
22 border-radius: 5px; 22 border-radius: 5px;
23 } 23 }
24 24
25 .login-form h1 { 25 .login-form h1 {
26 text-align: center; 26 text-align: center;
27 margin-bottom: 20px; 27 margin-bottom: 20px;
28 } 28 }
29 29
30 .login-form .form-group { 30 .login-form .form-group {
31 margin-bottom: 20px; 31 margin-bottom: 20px;
32 } 32 }
33 33
34 .login-form label { 34 .login-form label {
35 display: block; 35 display: block;
36 font-weight: bold; 36 font-weight: bold;
37 } 37 }
38 38
39 .login-form input[type="text"], 39 .login-form input[type="text"],
40 .login-form input[type="password"] { 40 .login-form input[type="password"] {
41 width: 100%; 41 width: 100%;
42 padding: 10px; 42 padding: 10px;
43 border: 1px solid #ddd; 43 border: 1px solid #ddd;
44 border-radius: 5px; 44 border-radius: 5px;
45 } 45 }
46 46
47 .login-form .error { 47 .login-form .error {
48 color: red; 48 color: red;
49 } 49 }
50 50
51 .login-form button[type="submit"] { 51 .login-form button[type="submit"] {
52 padding: 20px 20px; 52 padding: 20px 20px;
53 background-color: #007bff; 53 background-color: #007bff;
54 color: #fff; 54 color: #fff;
55 border: none; 55 border: none;
56 border-radius: 5px; 56 border-radius: 5px;
57 cursor: pointer; 57 cursor: pointer;
58 } 58 }
59 59
60 </style> 60 </style>
61 </head> 61 </head>
62 <body> 62 <body>
63 <div class="login-form"> 63 <div class="login-form">
64 <img class="logo" src="{{ url_for('static', filename='img/logo-k2.png')}}" alt="" width="250"> 64 <img class="logo" src="{{ url_for('static', filename='img/logo-k2.png')}}" alt="" width="250">
65 <h1>K2CloudERP-2.5.1.27</h1> 65 <h1>K2CloudERP-2.5.1.27</h1>
66 66
67 <form method="post" action="/first-login"> 67 <form method="post" action="/first-login">
68 <div class="form-group"> 68 <div class="form-group">
69 <p><input type="text" name="username" placeholder="Ім'я користувача"></p> 69 <p><input type="text" name="username" placeholder="Ім'я користувача"></p>
70 </div> 70 </div>
71 <div class="form-group"> 71 <div class="form-group">
72 <p><input type="password" name="password" placeholder="Пароль"></p> 72 <p><input type="password" name="password" placeholder="Пароль"></p>
73 </div> 73 </div>
74 <button type="submit" class="btn btn-primary">ВХІД</button> 74 <button type="submit" class="btn btn-primary">ВХІД</button>
75 </form> 75 </form>
76 76
77 </div> 77 </div>
78 78
79 </body> 79 </body>
80 </html> 80 </html>
81 81