Compare View

switch
from
...
to
 
Commits (2)

Changes

Showing 53 changed files Inline Diff

File was created 1 /components/adm/
2 /components/grid/
3 /components/vue/
4
__pycache__/__init__.cpython-310.pyc
No preview for this file type
__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/adm/__pycache__/__init__.cpython-310.pyc
No preview for this file type
components/adm/adm/.idea/.gitignore
1 # Default ignored files File was deleted
2 /shelf/
3 /workspace.xml
4 # Editor-based HTTP Client requests
5 /httpRequests/
6 # Datasource local storage ignored files
7 /dataSources/
8 /dataSources.local.xml
9 1 # Default ignored files
components/adm/adm/.idea/adm.iml
1 <?xml version="1.0" encoding="UTF-8"?> File was deleted
2 <module type="PYTHON_MODULE" version="4">
3 <component name="NewModuleRootManager">
4 <content url="file://$MODULE_DIR$" />
5 <orderEntry type="inheritedJdk" />
6 <orderEntry type="sourceFolder" forTests="false" />
7 </component>
8 <component name="TemplatesService">
9 <option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
10 <option name="TEMPLATE_FOLDERS">
11 <list>
12 <option value="$MODULE_DIR$/templates" />
13 </list>
14 </option>
15 </component>
16 </module>
components/adm/adm/.idea/inspectionProfiles/Project_Default.xml
1 <component name="InspectionProjectProfileManager"> File was deleted
2 <profile version="1.0">
3 <option name="myName" value="Project Default" />
4 <inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
5 <inspection_tool class="JSHint" enabled="true" level="ERROR" enabled_by_default="true" />
6 <inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
7 <option name="ignoredPackages">
8 <value>
9 <list size="13">
10 <item index="0" class="java.lang.String" itemvalue="blinker" />
11 <item index="1" class="java.lang.String" itemvalue="greenlet" />
12 <item index="2" class="java.lang.String" itemvalue="Werkzeug" />
13 <item index="3" class="java.lang.String" itemvalue="Flask-WTF" />
14 <item index="4" class="java.lang.String" itemvalue="SQLAlchemy" />
15 <item index="5" class="java.lang.String" itemvalue="MarkupSafe" />
16 <item index="6" class="java.lang.String" itemvalue="click" />
17 <item index="7" class="java.lang.String" itemvalue="Flask-SQLAlchemy" />
18 <item index="8" class="java.lang.String" itemvalue="Jinja2" />
19 <item index="9" class="java.lang.String" itemvalue="Flask-Login" />
20 <item index="10" class="java.lang.String" itemvalue="WTForms" />
21 <item index="11" class="java.lang.String" itemvalue="itsdangerous" />
22 <item index="12" class="java.lang.String" itemvalue="Flask" />
23 </list>
24 </value>
25 </option>
26 </inspection_tool>
27 </profile>
28 </component>
components/adm/adm/.idea/inspectionProfiles/profiles_settings.xml
1 <component name="InspectionProjectProfileManager"> File was deleted
2 <settings>
3 <option name="USE_PROJECT_PROFILE" value="false" />
4 <version value="1.0" />
5 </settings>
6 </component>
components/adm/adm/.idea/misc.xml
1 <?xml version="1.0" encoding="UTF-8"?> File was deleted
2 <project version="4">
3 <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10" project-jdk-type="Python SDK" />
4 </project>
components/adm/adm/.idea/modules.xml
1 <?xml version="1.0" encoding="UTF-8"?> File was deleted
2 <project version="4">
3 <component name="ProjectModuleManager">
4 <modules>
5 <module fileurl="file://$PROJECT_DIR$/.idea/adm.iml" filepath="$PROJECT_DIR$/.idea/adm.iml" />
6 </modules>
7 </component>
8 </project>
components/adm/adm/.idea/vcs.xml
1 <?xml version="1.0" encoding="UTF-8"?> File was deleted
2 <project version="4">
3 <component name="VcsDirectoryMappings">
4 <mapping directory="$PROJECT_DIR$/.." vcs="Git" />
5 </component>
6 </project>
components/adm/adm/__pycache__/__init__.cpython-310.pyc
No preview for this file type
components/adm/adm/__pycache__/__init__.cpython-311.pyc
No preview for this file type
components/adm/adm/__pycache__/models.cpython-310.pyc
No preview for this file type
components/adm/adm/__pycache__/views.cpython-310.pyc
No preview for this file type
components/adm/adm/__pycache__/views.cpython-311.pyc
No preview for this file type
components/adm/adm/models.py
1 import uuid 1 import uuid
2 from sqlalchemy import Column, Integer, String 2 from sqlalchemy import Column, Integer, String
3 from sqlalchemy.ext.declarative import declarative_base 3 from sqlalchemy.ext.declarative import declarative_base
4 from flask_jwt_extended import create_access_token 4 from flask_jwt_extended import create_access_token, decode_token
5 from datetime import timedelta 5 from datetime import timedelta
6 import hashlib 6 import hashlib
7 from flask import current_app as k2 7 from flask import current_app as k2
8 8
9 9
10 Base = declarative_base() 10 Base = declarative_base()
11 11
12 class k2users(Base): 12 class k2users(Base):
13 __tablename__ = 'k2users' 13 __tablename__ = 'k2users'
14 user_id = Column(Integer, primary_key=True) 14 user_id = Column(Integer, primary_key=True)
15 login = Column(String(50)) 15 login = Column(String(50), unique=True)
16 name = Column(String(50)) 16 name = Column(String(50))
17 email = Column(String(150)) 17 email = Column(String(150))
18 password = Column(String(50)) 18 password = Column(String(50))
19 phone = Column(String(20)) 19 phone = Column(String(20))
20 firstname = Column(String(50))
21 roleid = Column(String(50))
20 22
21 def __init__(self, **kwargs): 23 def __init__(self, **kwargs):
22 self.user_id = hashlib.md5(str(uuid.uuid4()).encode()).hexdigest() 24 self.user_id = hashlib.md5(str(uuid.uuid4()).encode()).hexdigest()
23 self.login = kwargs.get('login') 25 self.login = kwargs.get('login')
24 self.password = hashlib.md5(kwargs.get('password').encode()).hexdigest() 26 self.password = hashlib.md5(kwargs.get('password').encode()).hexdigest()
25 self.name = kwargs.get('name') 27 self.name = kwargs.get('name')
26 self.email = kwargs.get('email') 28 self.email = kwargs.get('email')
27 self.phone = kwargs.get('phone') 29 self.phone = kwargs.get('phone')
30 self.firstname = kwargs.get('firstname')
31 self.roleid = kwargs.get('roleid')
32
28 33
29 def get_token(self, expire_time=24): 34 def get_token(self, expire_time=24):
30 expires_delta = timedelta(expire_time) 35 expires_delta = timedelta(expire_time)
31 token = create_access_token(identity=self.user_id, expires_delta=expires_delta) 36 token = create_access_token(identity=self.user_id, expires_delta=expires_delta)
32 return token 37 if self.roleid == '1':
38 role = 'admin'
39 else:
40 role = "client"
41
42 userData = {
43 "avatar": "/src/assets/images/avatars/avatar-4.png",
44 "email": self.email,
45 "fullName": self.name,
46 "id": self.user_id,
47 "role": role,
48 "username": self.login
49
50 }
51 if self.roleid != '1':
52 userAbilities = [
53 {
54 "action": "read",
55 "subject": "Auth"},
56
57 {
58 "action": "read",
59 "subject": "AclDemo"}
60 ]
61 else:
62 userAbilities = [
63 {
64 "action": "manage",
65 "subject": "all"
66 }
67 ]
68 return (token, userData,userAbilities)
33 69
34 @classmethod 70 @classmethod
35 def authenticate(cls, login, password): 71 def authenticate(cls, login, password):
36 session = k2.extensions['sqlalchemy'].db.session 72 session = k2.extensions['sqlalchemy'].db.session
37 user = '' 73 user = ''
38 try: 74 try:
39 user = session.query(cls).filter(cls.login == login).filter(cls.password == hashlib.md5(password.encode()).hexdigest()).one() 75 user = session.query(cls).filter(cls.login == login).filter(cls.password == hashlib.md5(password.encode()).hexdigest()).one()
40 except: 76 except:
41 user = None 77 user = None
78 return user
79
80 @classmethod
81 def user_by_login(cls, login):
82 session = k2.extensions['sqlalchemy'].db.session
83 user = session.query(cls).filter(cls.login == login).first()
84 return user
85
86
87
88
42 89
43 return user
components/adm/adm/views.py
1 import hashlib 1 import hashlib
2 import uuid 2 import uuid
3 from flask import Blueprint, request, jsonify 3 from flask import Blueprint, request, jsonify
4 from flask import current_app as k2 4 from flask import current_app as k2
5 from flask_jwt_extended import jwt_required, get_jwt_identity 5 from flask_jwt_extended import jwt_required, get_jwt_identity
6 # from flask_sqlalchemy import session 6
7 # from sqlalchemy.orm import sessionmaker
8 # from k2.k2inst import Component
9 7
10 from .models import k2users 8 from .models import k2users
11 9
12 10
13 adm = Blueprint('adm', __name__, template_folder='templates', static_folder='static', static_url_path='/adm') 11 adm = Blueprint('adm', __name__, template_folder='templates', static_folder='static', static_url_path='/adm')
14 12
15 13
14 @adm.route('/usersclients', methods=["GET"])
15 @jwt_required()
16 def users_clients():
17 return jsonify({"data": "roles"})
18
19 @adm.route('/menuaaccess', methods=["GET"])
20 @jwt_required()
21 def menu_aaccess():
22 return jsonify({"data": "roles"})
23
24 @adm.route('/users', methods=["GET"])
25 @jwt_required()
26 def grid_users():
27 return jsonify({"data": "users"})
28
29
30 @adm.route('/roles', methods=["GET"])
31 @jwt_required()
32 def grid_roles():
33 return jsonify({"data": "roles"})
34
35
16 @adm.route('/register', methods=["POST"]) 36 @adm.route('/register', methods=["POST"])
17 def register(): 37 def register():
18 params = request.json 38 params = request.json
19 session = k2.extensions['sqlalchemy'].db.session 39 session = k2.extensions['sqlalchemy'].db.session
20 exist = session.query(k2users).filter_by(login=request.json['login']).first() 40 exist = k2users.user_by_login(request.json['login'])
41 # exist = session.query(k2users).filter_by(login=request.json['login']).first()
42 if not exist:
21 if not exist: 43 user = k2users(**params)
22 user = k2users(**params) 44 session.add(user)
23 session.add(user) 45 session.commit()
24 session.commit() 46 token, userData, userAbilities = user.get_token()
25 token = user.get_token() 47 return {'accessToken': token, "userData": userData, "userAbilities": userAbilities}
26 return {'access_token': token} 48 else:
27 else: 49 return jsonify({'error': 'User exist'}), 401
28 return jsonify({'error': 'User exist'}), 401 50
29 51
30 52 @adm.route('/login', methods=["POST"])
31 @adm.route('/login', methods=["POST"]) 53 def login():
32 def login(): 54 params = request.json
33 params = request.json 55 user = k2users.authenticate(**params)
34 login = params.get('login')
35 password = params.get('password')
36 user = k2users.authenticate(login, password) 56 if user:
37 if user: 57 token, userData, userAbilities = user.get_token()
38 token = user.get_token() 58 return {'accessToken': token, "userData": userData, "userAbilities": userAbilities}
39 return {'access_token': token} 59 else:
40 else: 60 return jsonify({'error': 'Unauthorized'}), 401
41 return jsonify({'error': 'Unauthorized'}), 401 61
42 62
43 63 @adm.route('/logout', methods=["GET"])
44 @adm.route('/logout', methods=["GET"]) 64 @jwt_required()
45 @jwt_required() 65 def logout():
66
46 def logout(): 67 response = {
47 response = { 68 'message': 'Logged out successfully'
48 'message': 'Logged out successfully' 69 }
49 }
50 return jsonify(response)
51 70
52 71 return jsonify(response)
53 @adm.route('/users', methods=["GET"])
54 @jwt_required()
55 def grid_users():
56 return jsonify({"data": "users"})
57
58
59 @adm.route('/roles', methods=["GET"])
60 @jwt_required()
61 def grid_roles():
62 return jsonify({"data": "roles"})
63
64
65 @adm.route('/usersclients', methods=["GET"])
66 @jwt_required()
67 def users_clients():
68 return jsonify({"data": "roles"})
69
70 @adm.route('/menuaaccess', methods=["GET"])
71 @jwt_required()
72 def menu_aaccess():
73 return jsonify({"data": "roles"}) 72
74 73
75 74 @adm.route('/newUser', methods=["GET", "POST"])
76 @adm.route('/newUser', methods=["GET", "POST"]) 75 @jwt_required()
77 @jwt_required() 76 def newUser():
78 def newUser(): 77 session = k2.extensions['sqlalchemy'].db.session
79 session = k2.extensions['sqlalchemy'].db.session 78 exist = k2users.user_by_login(request.json['login'])
80 exist = session.query(k2users).filter_by(login=request.json['login']).first() 79 if not exist:
81 if not exist: 80 res = k2users(
82 res = k2users( 81 user_id=hashlib.md5(str(uuid.uuid4()).encode()).hexdigest(),
83 user_id=hashlib.md5(str(uuid.uuid4()).encode()).hexdigest(), 82 login=request.json['login'],
84 login=request.json['login'], 83 name=request.json['name'],
85 name=request.json['name'], 84 email=request.json['email'],
86 email=request.json['email'], 85 password=hashlib.md5(request.json['password'].encode()).hexdigest(),
87 password=hashlib.md5(request.json['password'].encode()).hexdigest(), 86 phone=request.json['phone'])
88 phone=request.json['phone']) 87 if res:
89 if res: 88 session.add(res)
90 session.add(res) 89 session.commit()
91 session.commit() 90 return jsonify({'status': 'ok'})
92 return jsonify({'status': 'ok'}) 91 else:
93 else: 92 return jsonify({'error': 'Such user exist'}), 404
94 return jsonify({'error': 'Such user exist'}), 404 93
95 94
96 95 @adm.route('/changePassword', methods=["POST"])
97 @adm.route('/changePassword', methods=["POST"]) 96 @jwt_required()
98 @jwt_required() 97 def changePassword():
99 def changePassword(): 98 session = k2.extensions['sqlalchemy'].db.session
100 session = k2.extensions['sqlalchemy'].db.session 99 current_user = get_jwt_identity()
101 current_user = get_jwt_identity() 100 user = session.query(k2users).filter_by(user_id=current_user).first()
102 user = session.query(k2users).filter_by(user_id=current_user).first() 101 if user.password == hashlib.md5(request.json['old_password'].encode()).hexdigest():
103 if user.password == hashlib.md5(request.json['old_password'].encode()).hexdigest(): 102 if request.json['new_password'] == request.json['re_new_password']:
104 if request.json['new_password'] == request.json['re_new_password']: 103 user.password = hashlib.md5(request.json['new_password'].encode()).hexdigest()
105 user.password = hashlib.md5(request.json['new_password'].encode()).hexdigest() 104 session.commit()
106 session.commit() 105 else:
107 else: 106 return jsonify({'error': 'Пароль не найден в базе'}), 404
108 return jsonify({'error': 'Пароль не найден в базе'}), 404 107 else:
109 else: 108 return jsonify({'error': 'Пароль не совпвдает'}), 404
110 return jsonify({'error': 'Пароль не совпвдает'}), 404 109 return jsonify({'status': 'ok'})
111 return jsonify({'status': 'ok'}) 110
components/adm/requirements.txt
1 alembic==1.11.1
2 Babel==2.12.1
3 bcrypt==4.0.1
4 bidict==0.22.1
1 alembic==1.11.1 5 blinker==1.6.2
6 certifi==2023.5.7
7 charset-normalizer==3.1.0
2 Babel==2.12.1 8 click==8.1.3
9 colorama==0.4.6
3 bcrypt==4.0.1 10 dnspython==2.3.0
4 bidict==0.22.1 11 email-validator==2.0.0.post2
5 blinker==1.6.2 12 Flask==2.3.2
13 flask-babel==3.1.0
14 Flask-Bcrypt==1.0.1
6 certifi==2023.5.7 15 Flask-Cors==3.0.10
7 charset-normalizer==3.1.0 16 Flask-JWT-Extended==4.5.2
8 click==8.1.3 17 Flask-Login==0.6.2
9 colorama==0.4.6 18 Flask-Migrate==4.0.4
10 dnspython==2.3.0 19 Flask-MySQLdb==1.0.1
11 email-validator==2.0.0.post2 20 Flask-SQLAlchemy==3.0.3
12 Flask==2.3.2 21 flask-staticdirs==1.0.1
13 flask-babel==3.1.0 22 Flask-WTF==1.1.1
14 Flask-Bcrypt==1.0.1 23 greenlet==2.0.2
15 Flask-Cors==3.0.10 24 idna==3.4
16 Flask-JWT-Extended==4.5.2 25 itsdangerous==2.1.2
17 Flask-Login==0.6.2 26 Jinja2==3.1.2
18 Flask-Migrate==4.0.4 27 jsonify==0.5
28 Mako==1.2.4
19 Flask-MySQLdb==1.0.1 29 MarkupSafe==2.1.2
30 mysql==0.0.3
31 mysql-connector==2.2.9
20 Flask-SQLAlchemy==3.0.3 32 mysql-connector-python==8.0.33
21 flask-staticdirs==1.0.1 33 mysqlclient==2.1.1
34 peppercorn==0.6
22 Flask-WTF==1.1.1 35 protobuf==3.20.3
23 greenlet==2.0.2 36 PyJWT==2.7.0
37 python-engineio==4.4.1
38 python-socketio==5.8.0
24 idna==3.4 39 pytz==2023.3
25 itsdangerous==2.1.2 40 PyYAML==6.0
41 requests==2.31.0
26 Jinja2==3.1.2 42 six==1.16.0
27 jsonify==0.5 43 SQLAlchemy==2.0.15
28 Mako==1.2.4 44 typing_extensions==4.6.2
45 urllib3==2.0.2
29 MarkupSafe==2.1.2 46 Werkzeug==2.3.4
30 mysql==0.0.3 47 WTForms==3.0.1
48
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 "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 msgid "New 2 page" 1 msgid "New 2 page"
32 msgstr "New 2 page" 2 msgstr "Нова сторінка 2"
3 msgid "title_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/static/default.css
No preview for this file type
components/k2test/k2test/static/style.css
No preview for this file type
components/k2test/k2test/templates/login.html
1 login File was deleted
2 1 login
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_login import login_user, current_user, logout_user, login_required 3 from flask_login import login_user, current_user, logout_user, login_required
3 from flask import current_app as k2 4 from flask import current_app as k2
4 from flask import Blueprint, json 5 from flask import Blueprint, json
5 from flask_sqlalchemy import SQLAlchemy 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',) 12 #component routes
10
11
12
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 36 # menu items for admin
37 @k2test.route('/menu-items') 37 @k2test.route('/menu-admin-items')
38 def menu_items(): 38 def menu_items():
39 data = [ 39 data = [
40 { 40 {
41 'title': 'Тестова компонента', 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/menu_items.yml
1 - url: File was deleted
2 name: new2
3 icon: mdi-chart-timeline-variant
4 title: Тест 2
5 category: Тестова компонента
6 - url:
7 name: new3
8 icon: mdi-chart-timeline-variant
9 title: Тест 3
10 category: Тестова компонента
11 - url:
12 name: new4
13 icon: mdi-chart-timeline-variant
14 title: Тест 4
15 category: Тестова компонента
16 1 - url:
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='Your Name', 7 author='k2 cloud',
8 author_email='yourname@example.com', 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
components/menu.yml
1 - heading: APPS & PAGES File was deleted
2 - children:
3 - title: "\u0422\u0435\u0441\u0442 3"
4 to: k2test-new3
5 - title: "\u0422\u0435\u0441\u0442 3"
6 to: k2test-new4
7 component_name: k2test
8 icon:
9 icon: mdi-chart-timeline-variant
10 id: 2
11 title: "\u0422\u0435\u0441\u0442\u043E\u0432\u0430 \u043A\u043E\u043C\u043F\u043E\
12 \u043D\u0435\u043D\u0442\u0430"
13 - children:
14 - title: "\u0417\u043C\u0456\u043D\u0430 \u043F\u0430\u0440\u043E\u043B\u044E"
15 to: adm-changePassword
16 - title: "\u041D\u043E\u0432\u0438\u0439 \u043A\u043E\u0440\u0438\u0441\u0442\u0443\
17 \u0432\u0430\u0447"
18 to: adm-newUser
19 - title: "\u041A\u043E\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0456"
20 to: adm-users
21 - title: "\u041A\u043E\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0456 \u043A\
22 \u043B\u0456\u0454\u043D\u0442\u0438"
23 to: adm-usersclients
24 - title: "\u041F\u0440\u0430\u0432\u0430"
25 to: adm-roles
26 - title: "\u0414\u043E\u0441\u0442\u0443\u043F \u0434\u043E \u043C\u0435\u043D\u044E"
27 to: adm-menuaaccess
28 component_name: adm
29 icon:
30 icon: mdi mdi-database
31 id: 3
32 title: "\u0410\u0434\u043C\u0456\u043D\u0456\u0441\u0442\u0440\u0443\u0432\u0430\
33 \u043D\u043D\u044F \u0441\u0430\u0439\u0442\u0443"
34 1 - heading: APPS & PAGES
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 class k2(k2obj): 14 class k2(k2obj):
13 version = '2.5.1.27' 15 version = '2.5.1.27'
14 db = SQLAlchemy() 16 db = SQLAlchemy()
15 domain = 'http://127.0.0.1:5000/' 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 venv_bin_path = os.path.join(os.path.dirname(sys.prefix), 'venv/Scripts') 19 venv_bin_path = os.path.join(os.path.dirname(sys.prefix), 'venv/Scripts')
18 json_sort_keys = False 20 json_sort_keys = False
19 timezone = pytz.timezone('Europe/Kiev') 21 timezone = pytz.timezone('Europe/Kiev')
20 secret_key = 'JH&INH987gFDHdsagh&8dwbjdw8ckw' 22 secret_key = 'JH&INH987gFDHdsagh&8dwbjdw8ckw'
21 default_language = 'en' 23 default_language = 'en'
22 current_language = '' 24 current_language = ''
23 babel_translation_directories = '' 25 babel_translation_directories = ''
26 components = ''
27
28
24 29
25 def __init__(self): 30 def __init__(self):
26 super().__init__() 31 super().__init__()
27 32
28 @classmethod 33 @classmethod
29 def load_babel_translation_directories(cls): 34 def load_babel_translation_directories(cls):
30 with open('languages/babel_translation_directories.yml', 'r') as f: 35 with open('languages/babel_translation_directories.yml', 'r') as f:
31 data = yaml.safe_load(f) 36 data = yaml.safe_load(f)
32 cls.babel_translation_directories = data['babel_translation_directories'] 37 cls.babel_translation_directories = data['babel_translation_directories']
33 38
34 @classmethod 39 @classmethod
35 def get_locale(cls): 40 def get_locale(cls):
36 if 'lang' in session: 41 if 'lang' in session:
37 # Отримати обрану мову з сеансу користувача 42 # Отримати обрану мову з сеансу користувача
38 return session['lang'] 43 return session['lang']
39 else: 44 else:
40 # Повернути мову за замовчуванням з налаштувань додатку 45 # Повернути мову за замовчуванням з налаштувань додатку
41 return cls.default_language #app.config['DEFAULT_LANGUAGE'] 46 return cls.default_language #app.config['DEFAULT_LANGUAGE']
42 47
43 @classmethod 48 @classmethod
44 def search_babel_translation_directories(cls): 49 def search_babel_translation_directories(cls):
45 response = requests.get(f'{cls.domain}api/languages') 50 response = requests.get(f'{cls.domain}api/languages')
46 if response.status_code == 200: 51 if response.status_code == 200:
47 cls.babel_translation_directories = response.text 52 cls.babel_translation_directories = response.text
48 data = {'babel_translation_directories': response.text} 53 data = {'babel_translation_directories': response.text}
49 else: 54 else:
50 cls.babel_translation_directories = 'languages' 55 cls.babel_translation_directories = 'languages'
51 data = {'babel_translation_directories': 'languages'} 56 data = {'babel_translation_directories': 'languages'}
52 57
53 with open('languages/babel_translation_directories.yml', 'w') as f: 58 with open('languages/babel_translation_directories.yml', 'w') as f:
54 yaml.dump(data, f) 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 # load database settings from yml file 61 # load database settings from yml file
63 def init_db_uri(self): 62 def init_db_uri(self):
64 with open('db/db.yml', 'r') as f: 63 with open('db/db.yml', 'r') as f:
65 db_config = yaml.load(f, Loader=yaml.FullLoader) 64 db_config = yaml.load(f, Loader=yaml.FullLoader)
66 return db_config 65 return db_config
67 66
68 # creating a connection 67 # creating a connection
69 def init_db(self, app): 68 def init_db(self, app):
70 db_config = self.init_db_uri() 69 db_config = self.init_db_uri()
71 db_uri = f"mysql+{db_config['driver']}://{db_config['user']}:{db_config['password']}@{db_config['host']}/{db_config['dbname']}" 70 db_uri = f"mysql+{db_config['driver']}://{db_config['user']}:{db_config['password']}@{db_config['host']}/{db_config['dbname']}"
72 app.config['SQLALCHEMY_DATABASE_URI'] = db_uri 71 app.config['SQLALCHEMY_DATABASE_URI'] = db_uri
73 db_path = '../db/database.db' 72 db_path = '../db/database.db'
74 app.config['SQLALCHEMY_BINDS'] = {'db2': f'sqlite:///{db_path}'} 73 app.config['SQLALCHEMY_BINDS'] = {'db2': f'sqlite:///{db_path}'}
75 self.db.init_app(app) 74 self.db.init_app(app)
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
41
k2/k2inst.py
1 import zipfile File was deleted
2 from flask import Flask, Blueprint, jsonify, render_template, redirect, url_for, session, g
3 import os
4 import shutil
5 from datetime import datetime
6 import uuid
7 from flask import request
8 from k2.k2cfg import k2
9 from flask_migrate import Migrate
10 import requests
11 import subprocess
12 from flask import current_app
13 import yaml
14 from functools import wraps
15 import os
16 import sys
17
18
19 # initialize db
20 db = k2.db
21 #migrate = Migrate(app, db)
22
23 def generate_id():
24 generated_id = uuid.uuid4().hex[:32]
25 return ''.join([generated_id[i:i + 4] for i in range(0, len(generated_id), 4)])
26 class Component(db.Model):
27 __bind_key__ = 'db2'
28 __tablename__ = 'component'
29 __table_args__ = {'extend_existing': True}
30 id = db.Column(db.String(36), primary_key=True, default=generate_id)
31 name = db.Column(db.String(100), nullable=False)
32 description = db.Column(db.String(200))
33 version = db.Column(db.String(20))
34 git_link = db.Column(db.String(200))
35 dependencies = db.Column(db.String)
36 date_installed = db.Column(db.DateTime, default=datetime.utcnow)
37 installed = db.Column(db.Boolean, default=False)
38
39 def __init__(self, name, description, version, git_link, dependencies, installed):
40 self.name = name
41 self.description = description
42 self.version = version
43 self.git_link = git_link
44 self.dependencies = dependencies
45 self.installed = installed
46 def __repr__(self):
47 return f"Component('{self.name}', '{self.version}')"
48 @classmethod
49 def get_repository_by_id(self,rep_id):
50 component = db.session.query(Component).filter(Component.id == rep_id).first()
51 return component
52 @classmethod
53 def get_repository_by_id(self,rep_id):
54 component = db.session.query(Component).filter(Component.id == rep_id).first()
55 return component
56 components_bp = Blueprint('components', __name__, template_folder='templates')
57 config = yaml.safe_load(open("db/first-login.yml"))
58
59 def first_login_required(view_func):
60 @wraps(view_func)
61 def decorated_view(*args, **kwargs):
62 if 'username' not in session:
63 return redirect(url_for('components.first_login'))
64 return view_func(*args, **kwargs)
65 return decorated_view
66
67 def append_to_yaml_file(file_path, data):
68 with open(file_path, 'r') as f:
69 existing_data = yaml.safe_load(f) or []
70
71 if not existing_data:
72 start_id = 1
73 else:
74 start_id = max(item.get('id', 0) for item in existing_data) + 1
75
76 for i, item in enumerate(data, start=start_id):
77 item['id'] = i
78 existing_data.append(item)
79
80 with open(file_path, 'w') as f:
81 yaml.dump(existing_data, f, default_flow_style=False)
82
83
84 def remove_from_yaml_file(file_path, component_name):
85 with open(file_path, 'r') as f:
86 existing_data = yaml.safe_load(f) or []
87
88 updated_data = [item for item in existing_data if item.get('component_name') != component_name]
89
90 with open(file_path, 'w') as f:
91 yaml.dump(updated_data, f, default_flow_style=False)
92
93 @components_bp.route('/api/languages', methods=['GET'])
94 def find_language():
95 components = Component.query.all()
96 languages_paths = []
97 # Додаємо головну директорію languages
98 languages_paths.append('languages')
99 # Знаходимо шляхи до директорій languages всередині папки components
100 for component in components:
101 component_languages_directory = 'components/' + component.name + '/' + component.name + '/languages'
102 languages_paths.append(component_languages_directory)
103 result = ';'.join(languages_paths)
104 print(result)
105 return result
106
107 @components_bp.route('/', methods=['GET', 'POST'])
108 @first_login_required
109 def home():
110 return redirect('/dashboard')
111
112 @components_bp.route('/login', methods=['GET', 'POST'])
113 def first_login():
114 if request.method == "POST":
115 username = request.form.get("username")
116 password = request.form.get("password")
117 for user in config["users"]:
118 if user["username"] == username and user["password"] == password:
119 session['username'] = username # Збереження ім'я користувача в сесії
120 return redirect(url_for('components.dashboard'))
121 return "Невірне ім'я користувача або пароль."
122 return render_template('first-login.html')
123
124 @components_bp.route('/logout')
125 def logout():
126 session.pop('username', None) # Видалення ім'я користувача з сесії
127 return redirect(url_for('components.first_login'))
128
129 @components_bp.route('/change_language/<lang>')
130 def change_language(lang):
131 session['lang'] = lang
132 k2.current_language = lang
133 return redirect(url_for('components.dashboard'))
134
135 @components_bp.route('/dashboard')
136 @first_login_required
137 def dashboard():
138
139 # Компоненти доступні для встановлення
140 try:
141 # GET-запит до API
142 response = requests.get(f'{k2.update_domain}api/components')
143 json_data = response.json()
144 # Перетворення JSON-об'єкту на масив
145 component_server = [item for item in json_data]
146 except:
147 component_server = None
148
149 # Встановлені компоненти
150 components = Component.query.all()
151 components_names = [component.name for component in components]
152 # Отримання значення пошукового запиту з параметрів URL
153 search_query = request.args.get('search_query')
154 #print(search_query)
155 filtered_components = []
156 if search_query:
157 filtered_components = [component for component in component_server if
158 (search_query.lower() in component['name'].lower() if component['name'] else False) or
159 (search_query.lower() in component['description'].lower() if component[
160 'description'] else False)]
161 else:
162 filtered_components = component_server
163 current_language = k2.current_language
164
165 return render_template('dashboard.html', components=components,
166 components_names=components_names, component_server=filtered_components,
167 search_query=search_query, language=k2.menu )
168
169 @components_bp.route('/show_components/<string:component_id>')
170 def show_components(component_id):
171 response = requests.get(f'{k2.update_domain}api/components')
172 json_data = response.json()
173 selected_component = next((component for component in json_data if component['id'] == component_id), None)
174 return render_template('component-info.html', selected_component=selected_component)
175
176 @components_bp.route('/install_components_git/<string:component_id>')
177 def install_components_git(component_id):
178 # Шлях до головної папки проекту
179 project_folder = 'components'
180 #component = Component.get_repository_by_id(component_id)
181 response = requests.get(f'{k2.update_domain}api/components')
182 json_data = response.json()
183 selected_component = next((component for component in json_data if component['id'] == component_id), None)
184 # Назва репозиторія
185 repository_name = selected_component['name']
186 # Шлях до папки репозиторія в межах проекту
187 repository_folder = os.path.join(project_folder, repository_name)
188 # URL репозиторія
189
190 git_repo_url = selected_component['git_link']
191 try:
192 # Перевірка наявності папки репозиторія
193 if not os.path.exists(repository_folder):
194 # Створення папки репозиторія
195 os.makedirs(repository_folder)
196 # Шлях до файлу __init__.py
197 init_file = os.path.join(repository_folder, '__init__.py')
198 # Перевірка наявності файлу __init__.py
199 if not os.path.exists(init_file):
200 # Створення пустого файлу __init__.py
201 open(init_file, 'a').close()
202 #підключення до приватного репозиторію
203 #os.environ['GITLAB_PRIVATE_TOKEN'] = '8xxTpxrKVDGoXD5ynjiW'
204 #git_repo_url_with_token = git_repo_url.replace('https://',
205 # f'https://oauth2:{os.environ["GITLAB_PRIVATE_TOKEN"]}@')
206
207 # Команда для встановлення з використанням git_repo_url і повного шляху до папки репозиторія
208 command = ['venv/Scripts/python.exe', '-m', 'pip', 'install', '--use-pep517', 'git+' + git_repo_url, '--target=' + repository_folder]
209 # Виконуємо команду встановлення
210 subprocess.check_call(command)
211
212 # Шлях до файлу, до якого потрібно додати код
213 file_path = 'routes.py'
214 # Код, який потрібно додати
215 code = selected_component['dependencies']
216 # Відкриття файлу у режимі дозапису
217 with open(file_path, 'a') as file:
218 # Запис нового коду у файл
219 file.write('\n')
220 file.write(code)
221 file.write('\n')
222 component = Component.query.filter_by(name=selected_component['name']).first()
223 if not component:
224 new_component = Component(
225 name=selected_component['name'],
226 description=selected_component['description'],
227 version=selected_component['version'],
228 git_link=selected_component['git_link'],
229 dependencies=selected_component['dependencies'],
230 installed=True
231 )
232 # Add the new component to the database
233 db.session.add(new_component)
234 db.session.commit()
235
236 # Повертаємо повідомлення про успішне встановлення
237 return f'Installed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />'
238 except subprocess.CalledProcessError as e:
239 return 'Error installing : ' + str(e)
240
241 @components_bp.route('/install_components/<string:component_id>')
242 def install_component_from_archive(component_id):
243 # Шлях до головної папки проекту
244 project_folder = 'components'
245 # Отримати відповідну компоненту зі списку компонент
246 response = requests.get(f'{k2.update_domain}api/components')
247 json_data = response.json()
248 selected_component = next((component for component in json_data if component['id'] == component_id), None)
249 if selected_component is None:
250 return 'Component not found'
251 # Отримати посилання на архів компоненти та версію
252 archive_url = selected_component['latest_component_data']
253 version = selected_component['latest_version']
254 try:
255 # Створити шлях до папки компоненти згідно назви та версії
256 component_folder = os.path.join(project_folder)
257 os.makedirs(component_folder, exist_ok=True)
258 # Завантажити архів компоненти
259 response = requests.get(archive_url, stream=True)
260 response.raise_for_status()
261 # Шлях до завантаженого архіву
262 archive_path = os.path.join(component_folder, f"{selected_component['name']}.zip")
263 # Встановити залежності з файлу requirements.txt
264
265 # Зберегти архів на диск
266 with open(archive_path, "wb") as file:
267 for chunk in response.iter_content(chunk_size=8192):
268 file.write(chunk)
269 # Розпакувати архів
270 with zipfile.ZipFile(archive_path, "r") as zip_ref:
271 zip_ref.extractall(component_folder)
272
273 # Видалити архів
274 os.remove(archive_path)
275
276 # Шлях до файлу, до якого потрібно додати код
277 file_path = 'routes.py'
278 # Код, який потрібно додати
279 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']}')'''
280 #selected_component['dependencies']
281
282 # Відкриття файлу у режимі дозапису
283 with open(file_path, 'a') as file:
284 # Запис нового коду у файл
285 file.write('\n')
286 file.write(code)
287 file.write('\n')
288
289 #Update database with the installed component
290 component = Component.query.filter_by(name=selected_component['name']).first()
291 if not component:
292 component = Component(
293 name=selected_component['name'],
294 description=selected_component['description'],
295 version=version,
296 git_link=selected_component['git_link'],
297 dependencies=code,
298 installed=True
299 )
300 db.session.add(component)
301 else:
302 component.version = version
303 component.git_link = selected_component['git_link']
304 component.dependencies = code
305 component.installed = True
306
307 db.session.commit()
308 # add menu items yml
309 #file_path_input = f"components/{selected_component['name']}/menu_items.yml"
310 #if os.path.exists(file_path_input):
311 # with open(file_path_input, 'r', encoding='utf-8') as file:
312 # data = yaml.safe_load(file)
313 #else:
314 # data = {
315 # 'url': {'name': 'error'},
316 # 'icon': 'error',
317 # 'title': f"{selected_component['name']}--error",
318 # 'category': 'error',
319 # }
320
321 #file_path = 'components/menu.yml'
322 #append_to_yaml_file(file_path, data)
323
324 #add language folders
325 k2.search_babel_translation_directories()
326 print(k2.domain)
327 return f'''Component installed successfully: {selected_component["name"]} v{version} {k2.babel_translation_directories},
328 \n \n please wait installing requirments...
329 <meta http-equiv="refresh" content="0;url=/install-requirments/{selected_component["name"]}" />'''
330
331 except Exception as e:
332 return f'Error installing component: {str(e)}'
333
334 @components_bp.route('/install-requirments/<string:selected_component_name>', methods=['GET'])
335 def install_requirments(selected_component_name):
336 #menu items api
337 response = requests.get(f"{k2.domain}{selected_component_name}/menu-items")
338 if response.status_code == 200:
339 data = response.json()
340
341 else:
342 data = []
343 # add component_name key for menu items
344 for item in data:
345 item['component_name'] = selected_component_name
346 file_path = 'components/menu.yml'
347 append_to_yaml_file(file_path, data)
348 k2.get_menu_elenents()
349
350 #install requirements
351 requirements_file = os.path.join('components', selected_component_name, "requirements.txt")
352 if os.path.isfile(requirements_file):
353 pip_command = f"{k2.venv_bin_path}/python -m pip install -r {requirements_file}"
354 subprocess.run(pip_command, shell=True, check=True)
355 return f'''Requirements installed successfully
356 \n \n please wait installing requirments components...
357 <meta http-equiv="refresh" content="1;url=/install-requirments-components" />'''
358
359 @components_bp.route('/install-requirments-components')
360 def install_requirements_components():
361 # Откриття файлу requirements_components.txt
362 requirements_file = 'requirements_components.txt'
363 component_ids = None
364 if os.path.isfile(requirements_file):
365 with open('requirements_components.txt', 'r') as file:
366 component_ids = file.read().splitlines()
367 if component_ids:
368 for component_id in component_ids:
369 # Виклик роуту '/install_components/<string:component_name>' для кожної назви компоненти
370 response = requests.get(
371 f'/install_components/{component_id}')
372 return f'Requirements components installed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />'
373 else:
374 return f'<meta http-equiv="refresh" content="1;url=/dashboard" />'
375 # Опрацювання відповіді (за потреби)
376
377
378 @components_bp.route('/remove_dependencies/<string:component_id>', methods=['GET'])
379 def remove_dependencies(component_id):
380 # Знаходимо компоненту за її ID
381 component = Component.query.get(component_id)
382 if not component:
383 return 'Component not found <meta http-equiv="refresh" content="1;url=/dashboard" />'
384 #remove menu items
385 component_name = component.name
386 file_path = "components/menu.yml"
387 remove_from_yaml_file(file_path, component_name)
388
389 # remove routes
390 file_path = 'routes.py'
391 with open(file_path, 'r') as file:
392 lines = file.readlines()
393
394 updated_lines = [line for line in lines if line.strip() not in component.dependencies]
395 with open(file_path, 'w') as file:
396 file.writelines(updated_lines)
397
398 component.installed = False
399 db.session.commit()
400
401 return 'Component successfully turn off <meta http-equiv="refresh" content="1;url=/dashboard" />'
402
403 @components_bp.route('/add_dependencies/<string:component_id>', methods=['GET'])
404 def add_dependencies(component_id):
405
406 # Знаходимо компоненту за її ID
407 component = Component.query.get(component_id)
408 if not component:
409 return 'Component not found <meta http-equiv="refresh" content="1;url=/dashboard" />'
410
411 # Шлях до файлу, з якого потрібно видалити залежності
412 file_path = 'routes.py'
413 code = component.dependencies
414 # Відкриття файлу у режимі дозапису
415 with open(file_path, 'a') as file:
416 # Запис нового коду у файл
417 file.write(code)
418
419 # Оновлюємо статус компоненти
420 component.installed = True
421 db.session.commit()
422 return 'Dependencies added successfully <meta http-equiv="refresh" content="1;url=/dashboard" />'
423
424 @components_bp.route('/remove-component/<string:component_id>')
425 def remove_component(component_id):
426 # Шлях до головної папки проекту
427 project_folder = 'components'
428 component = Component.query.get(component_id)
429 # Назва репозиторія
430 repository_name = component.name
431 # Шлях до папки репозиторія в межах проекту
432 repository_folder = os.path.join(project_folder, repository_name)
433 try:
434 # Перевірка наявності папки репозиторія
435 if os.path.exists(repository_folder):
436 # Видалення папки репозиторія
437 shutil.rmtree(repository_folder)
438 # Видаляємо компоненту з бази
439 if component:
440 db.session.delete(component)
441 db.session.commit()
442
443 # Повертаємо повідомлення про успішне видалення
444 return 'Component removed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />'
445 except Exception as e:
446 # Повертаємо повідомлення про помилку видалення
447 return 'Error removing component: ' + str(e)
448
449 @components_bp.route('/api/main-menu/')
450 def project_menu():
451 return k2.menu
452
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 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, g
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
24 db = k2.db
25
26
27 def first_login_required(view_func):
28 @wraps(view_func)
29 def decorated_view(*args, **kwargs):
30 if 'username' not in session:
31 return redirect(url_for('components.first_login'))
32 return view_func(*args, **kwargs)
33 return decorated_view
34
35 def append_to_yaml_file(file_path, data):
36 with open(file_path, 'r') as f:
37 existing_data = yaml.safe_load(f) or []
38
39 if not existing_data:
40 start_id = 1
41 else:
42 start_id = max(item.get('id', 0) for item in existing_data) + 1
43
44 for i, item in enumerate(data, start=start_id):
45 item['id'] = i
46 existing_data.append(item)
47
48 with open(file_path, 'w') as f:
49 yaml.dump(existing_data, f, default_flow_style=False)
50
51
52 def remove_from_yaml_file(file_path, component_name):
53 with open(file_path, 'r') as f:
54 existing_data = yaml.safe_load(f) or []
55
56 updated_data = [item for item in existing_data if item.get('component_name') != component_name]
57
58 with open(file_path, 'w') as f:
59 yaml.dump(updated_data, f, default_flow_style=False)
60
61 @components_bp.route('/api/languages', methods=['GET'])
62 def find_language():
63 components = Component.query.all()
64 languages_paths = []
65 # Додаємо головну директорію languages
66 languages_paths.append('languages')
67 # Знаходимо шляхи до директорій languages всередині папки components
68 for component in components:
69 component_languages_directory = 'components/' + component.name + '/' + component.name + '/languages'
70 languages_paths.append(component_languages_directory)
71 result = ';'.join(languages_paths)
72 print(result)
73 return result
74
75 @components_bp.route('/api/add-to-menu', methods=['GET'])
76 def add_to_menu():
77 data = []
78 components = Component.query.all()
79 components_names = [component.name for component in components]
80 for components_names in components_names:
81 response = requests.get(f"{k2.domain}{components_names}/menu-admin-items")
82 print(response)
83 if response.status_code == 200:
84 data.append(response.json()[0])
85 else:
86 data
87 # add component_name key for menu items
88 for item in data:
89 item['component_name'] = components_names
90 k2.menu = data
91 return k2.menu
92
93 @components_bp.route('/home', methods=['GET', 'POST'])
94 @first_login_required
95 def home():
96 return redirect('/dashboard')
97
98 @components_bp.route('/first-login', methods=['GET', 'POST'])
99 def first_login():
100 if request.method == "POST":
101 username = request.form.get("username")
102 password = request.form.get("password")
103 for user in config["users"]:
104 if user["username"] == username and user["password"] == password:
105 session['username'] = username # Збереження ім'я користувача в сесії
106 return redirect(url_for('components.dashboard'))
107 return "Невірне ім'я користувача або пароль."
108 return render_template('first-login.html')
109
110
111 @components_bp.route('/change_language/<lang>')
112 def change_language(lang):
113 session['lang'] = lang
114 k2.current_language = lang
115 return redirect(url_for('components.dashboard'))
116
117 @components_bp.route('/dashboard')
118 @first_login_required
119 def dashboard():
120 # Компоненти доступні для встановлення
121 try:
122 # GET-запит до API
123 response = requests.get(f'{k2.update_domain}api/components')
124 json_data = response.json()
125 # Перетворення JSON-об'єкту на масив
126 component_server = [item for item in json_data]
127 except:
128 component_server = None
129
130 # Встановлені компоненти
131 components = Component.query.all()
132 components_names = [component.name for component in components]
133 # Отримання значення пошукового запиту з параметрів URL
134 search_query = request.args.get('search_query')
135 #print(search_query)
136 filtered_components = []
137 if search_query:
138 filtered_components = [component for component in component_server if
139 (search_query.lower() in component['name'].lower() if component['name'] else False) or
140 (search_query.lower() in component['description'].lower() if component[
141 'description'] else False)]
142 else:
143 filtered_components = component_server
144 current_language = k2.current_language
145
146 return render_template('dashboard.html', components=components,
147 components_names=components_names, component_server=filtered_components,
148 search_query=search_query, language=k2.menu )
149
150 @components_bp.route('/show_components/<string:component_id>')
151 def show_components(component_id):
152 response = requests.get(f'{k2.update_domain}api/components')
153 json_data = response.json()
154 selected_component = next((component for component in json_data if component['id'] == component_id), None)
155 return render_template('component-info.html', selected_component=selected_component)
156
157 @components_bp.route('/install_components_git/<string:component_id>')
158 def install_components_git(component_id):
159 # Шлях до головної папки проекту
160 project_folder = 'components'
161 #component = Component.get_repository_by_id(component_id)
162 response = requests.get(f'{k2.update_domain}api/components')
163 json_data = response.json()
164 selected_component = next((component for component in json_data if component['id'] == component_id), None)
165 # Назва репозиторія
166 repository_name = selected_component['name']
167 # Шлях до папки репозиторія в межах проекту
168 repository_folder = os.path.join(project_folder, repository_name)
169 # URL репозиторія
170
171 git_repo_url = selected_component['git_link']
172 try:
173 # Перевірка наявності папки репозиторія
174 if not os.path.exists(repository_folder):
175 # Створення папки репозиторія
176 os.makedirs(repository_folder)
177 # Шлях до файлу __init__.py
178 init_file = os.path.join(repository_folder, '__init__.py')
179 # Перевірка наявності файлу __init__.py
180 if not os.path.exists(init_file):
181 # Створення пустого файлу __init__.py
182 open(init_file, 'a').close()
183 #підключення до приватного репозиторію
184 #os.environ['GITLAB_PRIVATE_TOKEN'] = '8xxTpxrKVDGoXD5ynjiW'
185 #git_repo_url_with_token = git_repo_url.replace('https://',
186 # f'https://oauth2:{os.environ["GITLAB_PRIVATE_TOKEN"]}@')
187
188 # Команда для встановлення з використанням git_repo_url і повного шляху до папки репозиторія
189 command = ['venv/Scripts/python.exe', '-m', 'pip', 'install', '--use-pep517', 'git+' + git_repo_url, '--target=' + repository_folder]
190 # Виконуємо команду встановлення
191 subprocess.check_call(command)
192
193 # Шлях до файлу, до якого потрібно додати код
194 file_path = 'routes.py'
195 # Код, який потрібно додати
196 code = selected_component['dependencies']
197 # Відкриття файлу у режимі дозапису
198 with open(file_path, 'a') as file:
199 # Запис нового коду у файл
200 file.write('\n')
201 file.write(code)
202 file.write('\n')
203 component = Component.query.filter_by(name=selected_component['name']).first()
204 if not component:
205 new_component = Component(
206 name=selected_component['name'],
207 description=selected_component['description'],
208 version=selected_component['version'],
209 git_link=selected_component['git_link'],
210 dependencies=selected_component['dependencies'],
211 installed=True
212 )
213 # Add the new component to the database
214 db.session.add(new_component)
215 db.session.commit()
216
217 # Повертаємо повідомлення про успішне встановлення
218 return f'Installed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />'
219 except subprocess.CalledProcessError as e:
220 return 'Error installing : ' + str(e)
221
222 @components_bp.route('/install_components/<string:component_id>')
223 def install_component_from_archive(component_id):
224 # Шлях до головної папки проекту
225 project_folder = 'components'
226 # Отримати відповідну компоненту зі списку компонент
227 response = requests.get(f'{k2.update_domain}api/components')
228 json_data = response.json()
229 selected_component = next((component for component in json_data if component['id'] == component_id), None)
230 if selected_component is None:
231 return 'Component not found'
232 # Отримати посилання на архів компоненти та версію
233 archive_url = selected_component['latest_component_data']
234 version = selected_component['latest_version']
235 try:
236 # Створити шлях до папки компоненти згідно назви та версії
237 component_folder = os.path.join(project_folder)
238 os.makedirs(component_folder, exist_ok=True)
239 # Завантажити архів компоненти
240 response = requests.get(archive_url, stream=True)
241 response.raise_for_status()
242 # Шлях до завантаженого архіву
243 archive_path = os.path.join(component_folder, f"{selected_component['name']}.zip")
244 # Встановити залежності з файлу requirements.txt
245
246 # Зберегти архів на диск
247 with open(archive_path, "wb") as file:
248 for chunk in response.iter_content(chunk_size=8192):
249 file.write(chunk)
250 # Розпакувати архів
251 with zipfile.ZipFile(archive_path, "r") as zip_ref:
252 zip_ref.extractall(component_folder)
253
254 # Видалити архів
255 os.remove(archive_path)
256
257 # Шлях до файлу, до якого потрібно додати код
258 file_path = 'routes.py'
259 # Код, який потрібно додати
260 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']}')'''
261 #selected_component['dependencies']
262
263 # Відкриття файлу у режимі дозапису
264 with open(file_path, 'a') as file:
265 # Запис нового коду у файл
266 file.write('\n')
267 file.write(code)
268 file.write('\n')
269
270 #Update database with the installed component
271 component = Component.query.filter_by(name=selected_component['name']).first()
272 if not component:
273 component = Component(
274 name=selected_component['name'],
275 description=selected_component['description'],
276 version=version,
277 git_link=selected_component['git_link'],
278 dependencies=code,
279 installed=True
280 )
281 db.session.add(component)
282 else:
283 component.version = version
284 component.git_link = selected_component['git_link']
285 component.dependencies = code
286 component.installed = True
287
288 db.session.commit()
289
290 #add language folders
291 k2.search_babel_translation_directories()
292 return f'''Component installed successfully: {selected_component["name"]} v{version} {k2.babel_translation_directories},
293 \n \n please wait installing requirments...
294 <meta http-equiv="refresh" content="0;url=/install-requirments/{selected_component["name"]}" />'''
295
296 except Exception as e:
297 return f'Error installing component: {str(e)}'
298
299 @components_bp.route('/install-requirments/<string:selected_component_name>', methods=['GET'])
300 def install_requirments(selected_component_name):
301 #menu items api
302 response = requests.get(f"{k2.domain}{selected_component_name}/menu-items")
303 if response.status_code == 200:
304 data = response.json()
305
306 else:
307 data = []
308 # add component_name key for menu items
309 for item in data:
310 item['component_name'] = selected_component_name
311 file_path = 'components/menu.yml'
312 append_to_yaml_file(file_path, data)
313 get_admin_menu()
314
315 #install requirements
316 requirements_file = os.path.join('components', selected_component_name, "requirements.txt")
317 if os.path.isfile(requirements_file):
318 pip_command = f"{k2.venv_bin_path}/python -m pip install -r {requirements_file}"
319 subprocess.run(pip_command, shell=True, check=True)
320 return f'''Requirements installed successfully
321 \n \n please wait installing requirments components...
322 <meta http-equiv="refresh" content="1;url=/install-requirments-components" />'''
323
324 @components_bp.route('/install-requirments-components')
325 def install_requirements_components():
326 # Откриття файлу requirements_components.txt
327 requirements_file = 'requirements_components.txt'
328 component_ids = None
329 if os.path.isfile(requirements_file):
330 with open('requirements_components.txt', 'r') as file:
331 component_ids = file.read().splitlines()
332 if component_ids:
333 for component_id in component_ids:
334 # Виклик роуту '/install_components/<string:component_name>' для кожної назви компоненти
335 response = requests.get(
336 f'/install_components/{component_id}')
337 return f'Requirements components installed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />'
338 else:
339 return f'<meta http-equiv="refresh" content="1;url=/dashboard" />'
340 # Опрацювання відповіді (за потреби)
341
342 @components_bp.route('/remove_dependencies/<string:component_id>', methods=['GET'])
343 def remove_dependencies(component_id):
344 # Знаходимо компоненту за її ID
345 component = Component.query.get(component_id)
346 if not component:
347 return 'Component not found <meta http-equiv="refresh" content="1;url=/dashboard" />'
348 #remove menu items
349 component_name = component.name
350 file_path = "components/menu.yml"
351 remove_from_yaml_file(file_path, component_name)
352
353 # remove routes
354 file_path = 'routes.py'
355 with open(file_path, 'r') as file:
356 lines = file.readlines()
357
358 updated_lines = [line for line in lines if line.strip() not in component.dependencies]
359 with open(file_path, 'w') as file:
360 file.writelines(updated_lines)
361
362 component.installed = False
363 db.session.commit()
364
365 return 'Component successfully turn off <meta http-equiv="refresh" content="1;url=/dashboard" />'
366
367 @components_bp.route('/add_dependencies/<string:component_id>', methods=['GET'])
368 def add_dependencies(component_id):
369
370 # Знаходимо компоненту за її ID
371 component = Component.query.get(component_id)
372 if not component:
373 return 'Component not found <meta http-equiv="refresh" content="1;url=/dashboard" />'
374
375 # Шлях до файлу, з якого потрібно видалити залежності
376 file_path = 'routes.py'
377 code = component.dependencies
378 # Відкриття файлу у режимі дозапису
379 with open(file_path, 'a') as file:
380 # Запис нового коду у файл
381 file.write(code)
382
383 # Оновлюємо статус компоненти
384 component.installed = True
385 db.session.commit()
386 return 'Dependencies added successfully <meta http-equiv="refresh" content="1;url=/dashboard" />'
387
388 @components_bp.route('/remove-component/<string:component_id>')
389 def remove_component(component_id):
390 # Шлях до головної папки проекту
391 project_folder = 'components'
392 component = Component.query.get(component_id)
393 # Назва репозиторія
394 repository_name = component.name
395 # Шлях до папки репозиторія в межах проекту
396 repository_folder = os.path.join(project_folder, repository_name)
397 try:
398 # Перевірка наявності папки репозиторія
399 if os.path.exists(repository_folder):
400 # Видалення папки репозиторія
401 shutil.rmtree(repository_folder)
402 # Видаляємо компоненту з бази
403 if component:
404 db.session.delete(component)
405 db.session.commit()
406
407 # Повертаємо повідомлення про успішне видалення
408 return 'Component removed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />'
409 except Exception as e:
410 # Повертаємо повідомлення про помилку видалення
411 return 'Error removing component: ' + str(e)
412
413 @components_bp.route('/api/main-menu/')
414 def get_admin_menu():
415 requests.get(f"{k2.domain}api/add-to-menu")
416 return k2.menu
417
languages/babel_translation_directories.yml
1 babel_translation_directories: languages;components/k2test/k2test/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 k2.k2inst import * 3 from flask_jwt_extended import JWTManager
4 from flask import Flask 4 #from k2.k2inst import *
5 from k2.k2cfg import k2 5 from flask import Flask
6 6 from k2.k2cfg import k2
7 # configure flask app 7
8 app = Flask(__name__) 8 # configure flask app
9 app.config['SECRET_KEY'] = k2.secret_key 9 app = Flask(__name__)
10 #app.config.from_object(k2) 10 app.config['SECRET_KEY'] = k2.secret_key
11 11 app.json.sort_keys = k2.json_sort_keys
12 # init db 12 #app.config.from_object(k2)
13 k2().init_db(app) 13
14 CORS(app) 14 # init db
15 15 k2().init_db(app)
16 #translate 16 CORS(app)
17 k2.load_babel_translation_directories() 17 jwt = JWTManager(app)
18 app.config['BABEL_TRANSLATION_DIRECTORIES'] = k2.babel_translation_directories 18
19 babel = Babel(app) 19 #translate
20 babel.init_app(app, locale_selector=k2.get_locale) 20 k2.load_babel_translation_directories()
21 21 app.config['BABEL_TRANSLATION_DIRECTORIES'] = k2.babel_translation_directories
22 #menu 22 babel = Babel(app)
23 k2.get_menu_elenents() 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
31
32
1 from flask import Flask
2 #app = Flask(__name__) 1 #app = Flask(__name__)
3 from main import app, babel 2 from main import app
4 from k2.k2inst import components_bp 3 from k2.k2rout import components_bp
5 app.register_blueprint(components_bp) 4 app.register_blueprint(components_bp)
6 from components.k2test.k2test.views import k2test
7 app.register_blueprint(k2test, url_prefix='/k2test') 5 from components.k2test.k2test.views import k2test
8 6 app.register_blueprint(k2test, url_prefix='/k2test')
9 from components.adm.adm.views import adm
10 app.register_blueprint(adm, url_prefix='/adm')
11
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">{{_('Components Shop')}}</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="/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
templates/index.html
1 <!DOCTYPE html> File was deleted
2 <html lang="">
3 <head>
4 <meta charset="utf-8" />
5 <meta http-equiv="X-UA-Compatible" content="IE=edge" />
6 <meta name="viewport" content="width=device-width,initial-scale=1" />
7 <title>Головна сторінка</title>
8 <link rel="stylesheet" href="{{ url_for('static', filename='grid/css/chunk-vendors.3bee5491.css') }}">
9 </head>
10 <body>
11 <h1>Index Main</h1>
12 <div id="app"></div>
13
14 <div>{{ name }}</div>
15 {% for js_file in js_files %}
16 <script src="{{ url_for('static', filename=js_file) }}"></script>
17 {% endfor %}
18 </body>
19 </html>
20 1 <!DOCTYPE html>