Browse Source

v8.0: Delete check_version.html and refactor version checking logic

Removed the `check_version.html` template and moved version checking to `script.js`. This refactoring simplifies the codebase by consolidating version check handling into JavaScript, removing deprecated HTML templates, and enhancing the backend logic and API routes.
pull/399/head
Aidaho 3 months ago
parent
commit
a7a8b4b10d
  1. 24
      app/api/routes/routes.py
  2. 2
      app/create_db.py
  3. 1
      app/jobs.py
  4. 1
      app/modules/common/common_classes.py
  5. 12
      app/modules/db/backup.py
  6. 6
      app/modules/db/cred.py
  7. 4
      app/modules/db/server.py
  8. 17
      app/modules/roxywi/class_models.py
  9. 31
      app/modules/roxywi/roxy.py
  10. 32
      app/modules/server/ssh.py
  11. 109
      app/modules/service/backup.py
  12. 6
      app/modules/service/installation.py
  13. 3
      app/modules/tools/alerting.py
  14. 5
      app/routes/admin/routes.py
  15. 27
      app/routes/main/routes.py
  16. 32
      app/routes/server/routes.py
  17. 5
      app/scripts/ansible/roles/docker/handlers/main.yml
  18. 14
      app/scripts/ansible/roles/git_backup.yml
  19. 42
      app/static/js/backup.js
  20. 15
      app/static/js/script.js
  21. 18
      app/templates/ajax/check_version.html
  22. 12
      app/templates/ajax/load_updateroxywi.html
  23. 4
      app/templates/ajax/new_git.html
  24. 6
      app/templates/include/admin_ssh.html
  25. 152
      app/views/server/backup_vews.py
  26. 319
      app/views/server/cred_views.py
  27. 344
      app/views/server/views.py
  28. 1
      requirements.txt

24
app/api/routes/routes.py

@ -2,13 +2,12 @@ from flask_swagger import swagger
from flask import jsonify, render_template, abort
from flask_pydantic import validate
from app import app, jwt
from app import app
from app.api.routes import bp
from app.views.install.views import InstallView
from app.views.server.views import (
ServerView, CredView, CredsView, ServerGroupView, ServerGroupsView, ServersView, ServerIPView
)
from app.views.server.backup_vews import BackupView, S3BackupView
from app.views.server.views import ServerView, ServerGroupView, ServerGroupsView, ServersView, ServerIPView
from app.views.server.cred_views import CredView, CredsView
from app.views.server.backup_vews import BackupView, S3BackupView, GitBackupView
from app.views.service.views import ServiceView, ServiceActionView, ServiceBackendView, ServiceConfigView, ServiceConfigVersionsView
from app.views.ha.views import HAView, HAVIPView, HAVIPsView
from app.views.user.views import UserView, UserGroupView, UserRoles
@ -30,20 +29,10 @@ def before_request():
pass
@jwt.expired_token_loader
def my_expired_token_callback(jwt_header, jwt_payload):
return jsonify(error="Token is expired"), 401
@jwt.unauthorized_loader
def custom_unauthorized_response(_err):
return jsonify(error="Authorize first"), 401
def register_api(view, endpoint, url, pk='listener_id', pk_type='int'):
view_func = view.as_view(endpoint)
bp.add_url_rule(url, view_func=view_func, methods=['POST'])
bp.add_url_rule(f'{url}/<{pk_type}:{pk}>', view_func=view_func, methods=['GET', 'PUT', 'DELETE'])
bp.add_url_rule(f'{url}/<{pk_type}:{pk}>', view_func=view_func, methods=['GET', 'PUT', 'PATCH', 'DELETE'])
def register_api_id_ip(view, endpoint, url: str = '', methods: list = ['GET', 'POST']):
@ -73,9 +62,10 @@ register_api_id_ip(ServiceActionView, 'service_action', '/<any(start, stop, relo
register_api(ServerView, 'server', '/server', 'server_id')
register_api(BackupView, 'backup_fs', '/server/backup/fs', 'backup_id')
register_api(S3BackupView, 'backup_s3', '/server/backup/s3', 'backup_id')
register_api(GitBackupView, 'backup_git', '/server/backup/git', 'backup_id')
bp.add_url_rule('/server/<server_id>/ip', view_func=ServerIPView.as_view('server_ip_ip'), methods=['GET'])
bp.add_url_rule('/server/<int:server_id>/ip', view_func=ServerIPView.as_view('server_ip'), methods=['GET'])
register_api(CredView, 'cred', '/server/cred', 'creds_id')
register_api(CredView, 'cred', '/server/cred', 'cred_id')
bp.add_url_rule('/server/creds', view_func=CredsView.as_view('creds'), methods=['GET'])
bp.add_url_rule('/servers', view_func=ServersView.as_view('servers'), methods=['GET'])

2
app/create_db.py

@ -147,7 +147,7 @@ def default_values():
print(str(e))
try:
Groups.insert(name='Default', description='All servers are included in this group by default', group_id=1).on_conflict_ignore().execute()
Groups.insert(name='Default', description='All servers are included in this group by default', id=1).on_conflict_ignore().execute()
except Exception as e:
print(str(e))

1
app/jobs.py

@ -4,7 +4,6 @@ import datetime
from app import scheduler
import app.modules.db.sql as sql
import app.modules.db.user as user_sql
import app.modules.db.roxy as roxy_sql
import app.modules.db.history as history_sql
import app.modules.roxywi.roxy as roxy

1
app/modules/common/common_classes.py

@ -4,7 +4,6 @@ from flask import g
import app.modules.db.server as server_sql
import app.modules.roxywi.common as roxywi_common
from app.modules.roxywi.exception import RoxywiResourceNotFound
from app.modules.roxywi.class_models import ServerRequest, GroupQuery, CredRequest, ChannelRequest
from app.middleware import get_user_params

12
app/modules/db/backup.py

@ -48,9 +48,9 @@ def delete_s3_backups(backup_id: int) -> bool:
return True
def insert_new_git(server_id, service_id, repo, branch, period, cred, description) -> None:
def insert_new_git(server_id, service_id, repo, branch, period, cred, description) -> int:
try:
GitSetting.insert(
return GitSetting.insert(
server_id=server_id, service_id=service_id, repo=repo, branch=branch, period=period,
cred_id=cred, description=description
).execute()
@ -58,15 +58,11 @@ def insert_new_git(server_id, service_id, repo, branch, period, cred, descriptio
out_error(e)
def delete_git(git_id):
query = GitSetting.delete().where(GitSetting.id == git_id)
def delete_git(git_id: int) -> None:
try:
query.execute()
GitSetting.delete().where(GitSetting.id == git_id).execute()
except Exception as e:
out_error(e)
return False
else:
return True
def select_gits(**kwargs):

6
app/modules/db/cred.py

@ -4,7 +4,9 @@ from app.modules.roxywi.exception import RoxywiResourceNotFound
def select_ssh(**kwargs):
if kwargs.get("name") is not None:
if kwargs.get("group") and kwargs.get("cred_id"):
query = Cred.select().where((Cred.id == kwargs.get('cred_id')) & (Cred.group_id == kwargs.get('group')))
elif kwargs.get("name") is not None:
query = Cred.select().where(Cred.name == kwargs.get('name'))
elif kwargs.get("id") is not None:
query = Cred.select().where(Cred.id == kwargs.get('id'))
@ -16,6 +18,8 @@ def select_ssh(**kwargs):
query = Cred.select()
try:
query_res = query.execute()
except Cred.DoesNotExist:
raise RoxywiResourceNotFound
except Exception as e:
out_error(e)
else:

4
app/modules/db/server.py

@ -25,10 +25,10 @@ def delete_server(server_id):
return True
def update_server(hostname, group, type_ip, enable, master, server_id, cred, port, desc, firewall, protected):
def update_server(hostname, ip, group, type_ip, enable, master, server_id, cred, port, desc, firewall, protected):
try:
server_update = Server.update(
hostname=hostname, group_id=group, type_ip=type_ip, enabled=enable, master=master, cred_id=cred,
hostname=hostname, ip=ip, group_id=group, type_ip=type_ip, enabled=enable, master=master, cred_id=cred,
port=port, description=desc, firewall_enable=firewall, protected=protected
).where(Server.server_id == server_id)
server_update.execute()

17
app/modules/roxywi/class_models.py

@ -4,7 +4,7 @@ from typing import Optional, Annotated, Union, Literal, Any, Dict, List
from shlex import quote
from pydantic_core import CoreSchema, core_schema
from pydantic import BaseModel, field_validator, StringConstraints, IPvAnyAddress, AnyUrl, root_validator, GetCoreSchemaHandler
from pydantic import BaseModel, Base64Str, StringConstraints, IPvAnyAddress, AnyUrl, root_validator, GetCoreSchemaHandler
DomainName = Annotated[str, StringConstraints(pattern=r"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z][a-z0-9-]{0,61}[a-z0-9]$")]
@ -110,7 +110,7 @@ class ServerRequest(BaseModel):
description: Optional[EscapedString] = None
group_id: Optional[int] = None
protected: Optional[bool] = 0
master: Optional[int] = None
master: Optional[int] = 0
port: Annotated[int, Gt(1), Le(65535)] = 22
haproxy: Optional[bool] = 0
nginx: Optional[bool] = 0
@ -137,7 +137,7 @@ class CredRequest(BaseModel):
class CredUploadRequest(BaseModel):
private_key: str
private_key: Union[Base64Str, str]
passphrase: Optional[EscapedString] = None
@ -252,3 +252,14 @@ class S3BackupRequest(BaseModel):
access_key: Optional[EscapedString] = None
time: Optional[EscapedString] = None
description: Optional[EscapedString] = None
class GitBackupRequest(BaseModel):
server_id: int
service_id: int
init: Optional[bool] = 0
repo: Optional[EscapedString] = None
branch: Optional[EscapedString] = 'main'
time: Optional[EscapedString] = 'weekly'
cred_id: Optional[int] = None
description: Optional[EscapedString] = None

31
app/modules/roxywi/roxy.py

@ -1,5 +1,6 @@
import os
import re
from packaging import version
import distro
import requests
@ -26,33 +27,31 @@ def is_docker() -> bool:
return True
return False
def check_ver():
return roxy_sql.get_ver()
def versions():
json_data = {
'need_update': 0
}
try:
current_ver = check_ver()
current_ver_without_dots = current_ver.split('.')
current_ver_without_dots = ''.join(current_ver_without_dots)
current_ver_without_dots = current_ver_without_dots.replace('\n', '')
current_ver_without_dots = int(current_ver_without_dots)
except Exception:
current_ver = "Cannot get current version"
current_ver_without_dots = 0
current_ver = roxy_sql.get_ver()
json_data['current_ver'] = roxy_sql.get_ver()
except Exception as e:
raise Exception(f'Cannot get current version: {e}')
try:
new_ver = check_new_version('roxy-wi')
new_ver_without_dots = new_ver.split('.')
new_ver_without_dots = ''.join(new_ver_without_dots)
new_ver_without_dots = new_ver_without_dots.replace('\n', '')
new_ver_without_dots = int(new_ver_without_dots)
json_data['new_ver'] = new_ver
except Exception as e:
new_ver = "Cannot get a new version"
new_ver_without_dots = 0
roxywi_common.logging('Roxy-WI server', f' {e}', roxywi=1)
raise Exception(f'Cannot get new version: {e}')
if version.parse(current_ver) < version.parse(new_ver):
json_data['need_update'] = 1
return current_ver, new_ver, current_ver_without_dots, new_ver_without_dots
return json_data
def check_new_version(service):

32
app/modules/server/ssh.py

@ -1,8 +1,10 @@
import os
import base64
from cryptography.fernet import Fernet
import paramiko
from flask import render_template
from playhouse.shortcuts import model_to_dict
import app.modules.db.cred as cred_sql
import app.modules.db.group as group_sql
@ -153,7 +155,7 @@ def upload_ssh_key(ssh_id: int, key: str, passphrase: str) -> None:
roxywi_common.logging('RMON server', e.args[0], roxywi=1)
raise Exception(e)
if passphrase != "''":
if passphrase:
try:
passphrase = crypt_password(passphrase)
except Exception as e:
@ -244,3 +246,31 @@ def decrypt_password(password: str) -> str:
except Exception as e:
raise Exception(f'error: Cannot decrypt password: {e}')
return decryp_pass
def get_creds(group_id: int = None, cred_id: int = None) -> list:
json_data = []
lib_path = get_config.get_config_var('main', 'lib_path')
if group_id and cred_id:
creds = cred_sql.select_ssh(group=group_id, cred_id=cred_id)
elif group_id:
creds = cred_sql.select_ssh(group=group_id)
else:
creds = cred_sql.select_ssh()
for cred in creds:
cred_dict = model_to_dict(cred)
cred_dict['name'] = cred_dict['name'].replace("'", "")
ssh_key_file = f'{lib_path}/keys/{cred_dict["name"]}.pem'
if os.path.isfile(ssh_key_file):
with open(ssh_key_file, 'rb') as key:
cred_dict['private_key'] = base64.b64encode(key.read()).decode('utf-8')
else:
cred_dict['private_key'] = ''
if cred_dict['password']:
cred_dict['password'] = decrypt_password(cred_dict['password'])
if cred_dict['passphrase']:
cred_dict['passphrase'] = decrypt_password(cred_dict['passphrase'])
json_data.append(cred_dict)
return json_data

109
app/modules/service/backup.py

@ -1,3 +1,4 @@
from docutils.parsers.rst.directives import body
from flask import render_template
import app.modules.db.sql as sql
@ -8,7 +9,7 @@ import app.modules.db.service as service_sql
import app.modules.server.ssh as ssh_mod
import app.modules.roxywi.common as roxywi_common
import app.modules.service.installation as installation_mod
from app.modules.roxywi.class_models import BackupRequest, IdResponse, IdDataResponse, BaseResponse, S3BackupRequest
from app.modules.roxywi.class_models import BackupRequest, IdResponse, IdDataResponse, BaseResponse, S3BackupRequest, GitBackupRequest
def create_backup_inv(json_data: BackupRequest, del_id: int = 0) -> None:
@ -51,6 +52,29 @@ def create_s3_backup_inv(data: S3BackupRequest, tag: str) -> None:
raise Exception(f'error: {e}')
def create_git_backup_inv(data: GitBackupRequest, server_ip: str, service: str, del_job: int = 0) -> None:
service_config_dir = sql.get_setting(service + '_dir')
ssh_settings = ssh_mod.return_ssh_keys_path(server_ip, id=data.cred_id)
print('del_job',del_job)
inv = {"server": {"hosts": {}}}
inv["server"]["hosts"][server_ip] = {
"REPO": data.repo,
"CONFIG_DIR": service_config_dir,
"PERIOD": data.time,
"INIT": data.init,
"BRANCH": data.branch,
"SERVICE": service,
"DELJOB": del_job,
"KEY": ssh_settings['key']
}
try:
installation_mod.run_ansible(inv, [server_ip], 'git_backup')
except Exception as e:
raise Exception(f'error: {e}')
def create_backup(json_data: BackupRequest, is_api: bool) -> tuple:
if backup_sql.check_exists_backup(json_data.server):
raise Exception(f'warning: Backup job for {json_data.server} already exists')
@ -71,7 +95,6 @@ def create_backup(json_data: BackupRequest, is_api: bool) -> tuple:
return IdDataResponse(data=data, id=last_id).model_dump(mode='json'), 201
def delete_backup(json_data: BackupRequest, backup_id: int) -> tuple:
create_backup_inv(json_data, backup_id)
backup_sql.delete_backups(backup_id)
@ -88,13 +111,6 @@ def update_backup(json_data: BackupRequest, backup_id: int) -> tuple:
def create_s3_backup(data: S3BackupRequest, is_api: bool) -> tuple:
# if deljob:
# time = ''
# secret_key = ''
# access_key = ''
# tag = 'delete'
# else:
# tag = 'add'
if backup_sql.check_exists_s3_backup(data.server):
raise Exception(f'Backup job for {data.server} already exists')
@ -103,7 +119,6 @@ def create_s3_backup(data: S3BackupRequest, is_api: bool) -> tuple:
except Exception as e:
raise e
# if not deljob:
try:
last_id = backup_sql.insert_s3_backup_job(**data.model_dump(mode='json'))
roxywi_common.logging('backup ', f'a new S3 backup job for server {data.server} has been created', roxywi=1, login=1)
@ -115,11 +130,6 @@ def create_s3_backup(data: S3BackupRequest, is_api: bool) -> tuple:
else:
temp = render_template('ajax/new_s3_backup.html', backups=backup_sql.select_s3_backups(**data.model_dump(mode='json')))
return IdDataResponse(id=last_id, data=temp).model_dump(mode='json'), 201
# elif deljob:
# backup_sql.delete_s3_backups(deljob)
# roxywi_common.logging('backup ', f' a S3 backup job for server {server} has been deleted', roxywi=1, login=1)
# return 'ok'
def delete_s3_backup(data: S3BackupRequest, backup_id: int) -> None:
@ -131,46 +141,49 @@ def delete_s3_backup(data: S3BackupRequest, backup_id: int) -> None:
raise e
def git_backup(server_id, service_id, git_init, repo, branch, period, cred, del_job, description, backup_id) -> str:
server_ip = server_sql.select_server_ip_by_id(server_id)
service_name = service_sql.select_service_name_by_id(service_id).lower()
service_config_dir = sql.get_setting(service_name + '_dir')
ssh_settings = ssh_mod.return_ssh_keys_path(server_ip, id=cred)
if repo is None or git_init == '0':
repo = ''
if branch is None or branch == '0':
branch = 'main'
inv = {"server": {"hosts": {}}}
inv["server"]["hosts"][server_ip] = {
"REPO": repo,
"CONFIG_DIR": service_config_dir,
"PERIOD": period,
"INIT": git_init,
"BRANCH": branch,
"SERVICE": service_name,
"DELJOB": del_job,
"KEY": ssh_settings['key']
}
def create_git_backup(data: GitBackupRequest, is_api: bool) -> tuple:
server_ip = server_sql.select_server_ip_by_id(data.server_id)
service_name = service_sql.select_service_name_by_id(data.service_id).lower()
try:
create_git_backup_inv(data, server_ip, service_name)
except Exception as e:
raise Exception(e)
try:
installation_mod.run_ansible(inv, [server_ip], 'git_backup')
last_id = backup_sql.insert_new_git(server_id=data.server_id, service_id=data.service_id, repo=data.repo, branch=data.branch, period=data.time,
cred=data.cred_id, description=data.description)
roxywi_common.logging(server_ip, 'A new git job has been created', roxywi=1, login=1, keep_history=1,
service=service_name)
except Exception as e:
raise Exception(f'error: {e}')
raise Exception(e)
if not del_job:
backup_sql.insert_new_git(server_id=server_id, service_id=service_id, repo=repo, branch=branch, period=period, cred=cred, description=description)
if is_api:
return IdResponse(id=last_id).model_dump(mode='json'), 201
else:
kwargs = {
"gits": backup_sql.select_gits(server_id=server_id, service_id=service_id),
"sshs": cred_sql.select_ssh(),
"gits": backup_sql.select_gits(server_id=data.server_id, service_id=data.service_id),
"sshs": cred_sql.select_ssh(),
"servers": roxywi_common.get_dick_permit(),
"services": service_sql.select_services(),
"new_add": 1,
"lang": roxywi_common.get_user_lang_for_flask()
}
roxywi_common.logging(server_ip, 'A new git job has been created', roxywi=1, login=1, keep_history=1, service=service_name)
return render_template('ajax/new_git.html', **kwargs)
else:
if backup_sql.delete_git(backup_id):
return 'ok'
temp = render_template('ajax/new_git.html', **kwargs)
return IdDataResponse(id=last_id, data=temp).model_dump(mode='json'), 201
def delete_git_backup(data: GitBackupRequest, backup_id: int) -> tuple:
server_ip = server_sql.select_server_ip_by_id(data.server_id)
service_name = service_sql.select_service_name_by_id(data.service_id).lower()
try:
create_git_backup_inv(data, server_ip, service_name, 1)
except Exception as e:
raise Exception(e)
try:
backup_sql.delete_git(backup_id)
except Exception as e:
raise Exception(e)
return BaseResponse().model_dump(mode='json'), 204

6
app/modules/service/installation.py

@ -1,9 +1,9 @@
import os
import json
from typing import Union
from packaging import version
import distro
import ansible
import ansible_runner
@ -212,6 +212,8 @@ def run_ansible(inv: dict, server_ips: list, ansible_role: str) -> dict:
proxy = sql.get_setting('proxy')
proxy_serv = ''
tags = ''
python_int = '/usr/bin/python3' if distro.id() == 'ubuntu' else '/usr/bin/python3.11'
try:
agent_pid = server_mod.start_ssh_agent()
except Exception as e:
@ -261,7 +263,7 @@ def run_ansible(inv: dict, server_ips: list, ansible_role: str) -> dict:
'AWX_DISPLAY': False,
'SSH_AUTH_PID': agent_pid['pid'],
'SSH_AUTH_SOCK': agent_pid['socket'],
'ANSIBLE_PYTHON_INTERPRETER': '/usr/bin/python3'
'ANSIBLE_PYTHON_INTERPRETER': python_int
}
kwargs = {
'private_data_dir': '/var/www/haproxy-wi/app/scripts/ansible/',

3
app/modules/tools/alerting.py

@ -171,6 +171,7 @@ def send_email(email_to: str, subject: str, message: str) -> None:
def telegram_send_mess(mess, level, **kwargs):
token_bot = ''
channel_name = ''
proxy = sql.get_setting('proxy')
if kwargs.get('channel_id') == 0:
return
@ -180,8 +181,6 @@ def telegram_send_mess(mess, level, **kwargs):
else:
telegrams = channel_sql.get_receiver_by_ip('telegram', kwargs.get('ip'))
proxy = sql.get_setting('proxy')
for telegram in telegrams:
token_bot = telegram.token
channel_name = telegram.chanel_name

5
app/routes/admin/routes.py

@ -16,6 +16,7 @@ import app.modules.roxywi.auth as roxywi_auth
import app.modules.roxywi.common as roxywi_common
import app.modules.tools.smon as smon_mod
import app.modules.tools.common as tools_common
import app.modules.server.ssh as ssh_mod
from app.views.admin.views import SettingsView
bp.add_url_rule(
@ -41,12 +42,12 @@ def admin():
users = user_sql.select_users()
servers = server_sql.select_servers(full=1)
masters = server_sql.select_servers(get_master_servers=1)
sshs = cred_sql.select_ssh()
sshs = ssh_mod.get_creds()
else:
users = user_sql.select_users(group=user_group)
servers = roxywi_common.get_dick_permit(virt=1, disable=0, only_group=1)
masters = server_sql.select_servers(get_master_servers=1, uuid=g.user_params['user_id'])
sshs = cred_sql.select_ssh(group=user_group)
sshs = ssh_mod.get_creds(group_id=user_group)
kwargs = {
'lang': g.user_params['lang'],

27
app/routes/main/routes.py

@ -4,7 +4,7 @@ from flask import render_template, request, g, abort, jsonify, redirect, url_for
from flask_jwt_extended import jwt_required
from flask_pydantic.exceptions import ValidationError
from app import app, cache
from app import app, cache, jwt
from app.routes.main import bp
import app.modules.db.user as user_sql
import app.modules.db.server as server_sql
@ -26,6 +26,16 @@ def _jinja2_filter_datetime(date, fmt=None):
return common.get_time_zoned_date(date, fmt)
@jwt.expired_token_loader
def my_expired_token_callback(jwt_header, jwt_payload):
return jsonify(error="Token is expired"), 401
@jwt.unauthorized_loader
def custom_unauthorized_response(_err):
return jsonify(error="Authorize first"), 401
@app.errorhandler(ValidationError)
def handle_pydantic_validation_errors1(e):
errors = []
@ -70,6 +80,7 @@ def page_is_forbidden(e):
def page_not_found(e):
if 'api' in request.url:
return jsonify({'error': str(e)}), 404
get_user_params()
kwargs = {
'user_params': g.user_params,
'title': e,
@ -79,23 +90,15 @@ def page_not_found(e):
@app.errorhandler(405)
@get_user_params()
def method_not_allowed(e):
if 'api' in request.url:
return jsonify({'error': str(e)}), 405
kwargs = {
'user_params': g.user_params,
'title': e,
'e': e
}
return render_template('error.html', **kwargs), 405
return jsonify({'error': str(e)}), 405
@app.errorhandler(500)
@get_user_params()
def internal_error(e):
if 'api' in request.url:
return jsonify({'error': str(e)}), 500
get_user_params()
kwargs = {
'user_params': g.user_params,
'title': e,
@ -224,4 +227,4 @@ def service_history(service, server_ip):
@bp.route('/internal/show_version')
@cache.cached()
def show_roxywi_version():
return render_template('ajax/check_version.html', versions=roxy.versions())
return jsonify(roxy.versions())

32
app/routes/server/routes.py

@ -7,14 +7,15 @@ from app.routes.server import bp
import app.modules.db.cred as cred_sql
import app.modules.db.server as server_sql
import app.modules.db.backup as backup_sql
import app.modules.db.service as service_sql
import app.modules.common.common as common
import app.modules.roxywi.auth as roxywi_auth
import app.modules.roxywi.common as roxywi_common
import app.modules.server.server as server_mod
import app.modules.service.backup as backup_mod
from app.middleware import get_user_params
from app.views.server.views import ServerView, CredView, CredsView, ServerGroupView, ServerGroupsView, ServerIPView
from app.views.server.backup_vews import BackupView, S3BackupView
from app.views.server.views import ServerView, ServerGroupView, ServerGroupsView, ServerIPView
from app.views.server.cred_views import CredView, CredsView
from app.views.server.backup_vews import BackupView, S3BackupView, GitBackupView
def register_api(view, endpoint, url, pk='listener_id', pk_type='int'):
@ -33,6 +34,7 @@ bp.add_url_rule('/<server_id>/ip', view_func=ServerIPView.as_view('server_ip_ip'
bp.add_url_rule('/<int:server_id>/ip', view_func=ServerIPView.as_view('server_ip'), methods=['GET'])
bp.add_url_rule('/backup', view_func=BackupView.as_view('backup', False), methods=['POST'])
bp.add_url_rule('/backup/s3', view_func=S3BackupView.as_view('backup_s3', False), methods=['POST'])
bp.add_url_rule('/backup/git', view_func=GitBackupView.as_view('backup_git', False), methods=['POST'])
error_mess = roxywi_common.return_error_message()
@ -137,6 +139,7 @@ def load_backup():
kwargs = {
'sshs': cred_sql.select_ssh(group=user_group),
'servers': roxywi_common.get_dick_permit(virt=1, disable=0, only_group=1),
'services': service_sql.select_services(),
'backups': backup_sql.select_backups(),
's3_backups': backup_sql.select_s3_backups(),
'gits': backup_sql.select_gits(),
@ -145,26 +148,3 @@ def load_backup():
'user_subscription': roxywi_common.return_user_subscription(),
}
return render_template('include/admin_backup.html', **kwargs)
@bp.route('/git', methods=['DELETE', 'POST'])
def create_git_backup():
json_data = request.get_json()
server_id = int(json_data['server'])
service_id = int(json_data['service'])
git_init = int(json_data['init'])
repo = common.checkAjaxInput(json_data['repo'])
branch = common.checkAjaxInput(json_data['branch'])
period = common.checkAjaxInput(json_data['time'])
cred = int(json_data['cred'])
del_job = int(json_data['del_job'])
description = common.checkAjaxInput(json_data['desc'])
backup_id = ''
if request.method == 'DELETE':
backup_id = json_data['backup_id']
try:
data = backup_mod.git_backup(server_id, service_id, git_init, repo, branch, period, cred, del_job, description, backup_id)
return jsonify({'status': 'ok', 'data': data})
except Exception as e:
return roxywi_common.handle_json_exceptions(e, f'Cannot {request.method} git backup')

5
app/scripts/ansible/roles/docker/handlers/main.yml

@ -1,4 +1,7 @@
---
- name: restart rsyslog
service: name=rsyslog state=restarted
- name: restart docker
service: "name=docker state={{ docker_restart_handler_state }}"
ignore_errors: "{{ ansible_check_mode }}"
ignore_errors: "{{ ansible_check_mode }}"

14
app/scripts/ansible/roles/git_backup.yml

@ -11,7 +11,7 @@
- name: Fail if has been installed
fail:
msg="Git configuration not found. Initialize git at the beginning"
when: not register_name.stat.exists and not INIT and DELJOB
when: not register_name.stat.exists and not INIT and not DELJOB
- name: Install git
package:
@ -34,15 +34,16 @@
- name: Copy ssh file
copy:
src: '{{ KEY }}'
dest: '/home/{{ ansible_user }}/.ssh/id_rsa'
dest: '/home/{{ ansible_user }}/.ssh/git_{{ SERVICE }}_key'
mode: 0600
group: '{{ ansible_user }}'
owner: '{{ ansible_user }}'
force: no
force: yes
when: INIT
- name: Add write permissions
shell: "chmod o+wr -R {{ CONFIG_DIR }}"
when: not DELJOB
- name: Git init
shell: 'cd {{ CONFIG_DIR }} && git init'
@ -58,13 +59,18 @@
email = roxy-wi@.com
when: INIT
- name: Add dir exception
shell: 'git config --global --add safe.directory {{ CONFIG_DIR }}'
when: INIT
become: no
- name: Git add remote
shell: 'cd {{ CONFIG_DIR }} && git add --all . && git commit -m "Roxy-WI init repo" && git branch -M {{ BRANCH }} && git remote add origin {{ REPO }}'
when: INIT
become: no
- name: Git add push
shell: 'cd {{ CONFIG_DIR }} && GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no" git push -u origin {{ BRANCH }}'
shell: 'cd {{ CONFIG_DIR }} && GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no -i /home/{{ ansible_user }}/.ssh/git_{{ SERVICE }}_key" git push -u origin {{ BRANCH }}'
when: INIT
become: no

42
app/static/js/backup.js

@ -219,18 +219,17 @@ function addGit(dialog_id) {
valid = valid && checkLength(branch_div, "Branch name", 1);
if (valid) {
let jsonData = {
"server": server_div.val(),
"service": service_div.val(),
"server_id": server_div.val(),
"service_id": service_div.val(),
"init": git_init,
"repo": $('#git-repo').val(),
"branch": branch_div.val(),
"time": time_div.val(),
"cred": cred_div.val(),
"del_job": 0,
"cred_id": cred_div.val(),
"desc": $('#git-description').text(),
}
$.ajax({
url: "/server/git",
url: "/server/backup/git",
data: JSON.stringify(jsonData),
contentType: "application/json; charset=utf-8",
type: "POST",
@ -388,34 +387,35 @@ function removeS3Backup(id) {
function removeGit(id) {
$("#git-table-" + id).css("background-color", "#f2dede");
let jsonData = {
"backup_id": id,
"del_job": 1,
"init": 0,
"repo": 0,
"branch": 0,
"time": 0,
"cred": $('#git-credentials-id-' + id).text(),
"server": $('#git-server-id-' + id).text(),
"service": $('#git-service-id-' + id).text(),
"desc": '',
"cred_id": $('#git-credentials-id-' + id).text(),
"server_id": $('#git-server-id-' + id).text(),
"service_id": $('#git-service-id-' + id).text(),
}
$.ajax({
url: "/server/git",
url: api_prefix + "/server/backup/git/" + id,
data: JSON.stringify(jsonData),
contentType: "application/json; charset=utf-8",
type: "DELETE",
success: function (data) {
if (data.status === 'failed') {
toastr.error(data.error);
} else {
statusCode: {
204: function (xhr) {
$("#git-table-" + id).remove();
},
404: function (xhr) {
$("#git-table-" + id).remove();
}
},
success: function (data) {
if (data) {
if (data.status === "failed") {
toastr.error(data);
}
}
}
});
}
function updateBackup(id) {
toastr.clear();
if ($("#backup-type-" + id + " option:selected").val() == "-------" || $('#backup-rserver-' + id).val() == '' || $('#backup-rpath-' + id).val() == '') {
if ($("#backup-type-" + id + " option:selected").val() === "-------" || $('#backup-rserver-' + id).val() === '' || $('#backup-rpath-' + id).val() === '') {
toastr.error('All fields must be completed');
} else {
let jsonData = {

15
app/static/js/script.js

@ -1163,21 +1163,26 @@ function show_version() {
NProgress.configure({showSpinner: false});
$.ajax( {
url: "/internal/show_version",
contentType: "application/json; charset=utf-8",
success: function( data ) {
$('#version').html(data);
var showUpdates = $( "#show-updates" ).dialog({
if (data.need_update) {
$('#version').html('<span id="show-updates-button" class="new-version-exists" style="cursor: pointer;">v' + data.current_ver + '</span>');
} else {
$('#version').html('v' + data.current_ver);
}
let showUpdates = $("#show-updates").dialog({
autoOpen: false,
width: 600,
modal: true,
title: 'There is a new Roxy-WI version',
buttons: {
Close: function() {
$( this ).dialog( "close" );
Close: function () {
$(this).dialog("close");
clearTips();
}
}
});
$('#show-updates-button').click(function() {
$('#show-updates-button').click(function () {
showUpdates.dialog('open');
});
}

18
app/templates/ajax/check_version.html

@ -1,18 +0,0 @@
{%- if versions is defined -%}
{%- set current_ver = versions.0 -%}
{%- set new_ver = versions.1 %}
{%- set current_ver_without_dots = versions.2 %}
{%- set new_ver_without_dots = versions.3 %}
{%- endif -%}
{%- if new_ver_without_dots is defined and current_ver_without_dots is defined and new_ver is defined and new_ver_without_dots is defined -%}
<a style="color: #000; cursor: pointer;">
{%- if new_ver_without_dots > current_ver_without_dots and new_ver != "Sorry cannot get current version" %}
<span id="show-updates-button" class="new-version-exists">v{{current_ver}}</span>
<script defer src="/static/js/fontawesome.min.js"></script>
{%- else %}
<a href="/admin#updatehapwi" title="Update center" style="color: black;">v{{current_ver}}</a>
{%- endif %}
</a>
{%- else %}
<a href="/admin#updatehapwi" title="Update center" style="color: black;">v{{current_ver}}</a>
{% endif -%}

12
app/templates/ajax/load_updateroxywi.html

@ -1,8 +1,4 @@
{% import 'languages/'+lang|default('en')+'.html' as lang %}
{% set current_ver = versions.0 %}
{% set new_ver = versions.1 %}
{% set current_ver_without_dots = versions.2 %}
{% set new_ver_without_dots = versions.3 %}
{% set services_name = {
'roxy-wi-checker': { 'link': 'checker', 'name': 'Checker', 'desc': lang.admin_page.desc.checker_desc },
'roxy-wi-keep_alive': { 'link': 'auto_start', 'name': 'Auto start', 'desc': lang.admin_page.desc.auto_start_desc },
@ -19,7 +15,7 @@
</td>
<td>
<b
{% if new_ver_without_dots > current_ver_without_dots and new_ver != "Sorry cannot get current version" %}
{% if versions.need_update %}
title=lang.admin_page.desc.a_new_version+" Roxy-WI"
style="color: var(--red-color)"
{% else %}
@ -27,14 +23,14 @@
style="color: var(--green-color)"
{% endif %}
>
{{current_ver}}
{{versions.current_ver}}
</b>
</td>
<td class="padding10">
<b>{{new_ver}}</b>
<b>{{versions.new_ver}}</b>
</td>
<td>
{% if new_ver_without_dots > current_ver_without_dots and new_ver != "Sorry cannot get current version" %}
{% if versions.need_update %}
<a class="ui-button ui-widget ui-corner-all" onclick="updateService('roxy-wi')" title="{{lang.words.w_update|title()}} Roxy-WI">{{lang.words.w_update|title()}}</a>
{% endif %}
</td>

4
app/templates/ajax/new_git.html

@ -38,7 +38,7 @@
</td>
<td style="width: 10%">
{% for ssh in sshs %}
{% if ssh.enable == 1 %}
{% if ssh.key_enabled == 1 %}
{% if ssh.id == b.cred_id %}
<span id="git-credentials-id-{{b.id}}" style="display: none">{{ ssh.id }}</span>
<span id="git-credentials-{{b.id}}">{{ ssh.name }}</span>
@ -47,7 +47,7 @@
{% endfor %}
</td>
<td style="width: 100%">
<span type="text" id="git-description-{{b.id}}">
<span id="git-description-{{b.id}}">
{% if b.description %}
{{b.description}}
{% endif %}

6
app/templates/include/admin_ssh.html

@ -20,7 +20,7 @@
<tr style="width: 50%;" id="ssh-table-{{ssh.id}}" class="{{ loop.cycle('odd', 'even') }}">
<td class="first-collumn padding10">
{% set id = 'ssh_name-' + ssh.id|string() %}
{{ input(id, value=ssh.name, size='15') }}
{{ input(id, value=ssh.name.replace("'", ""), size='15') }}
</td>
<td class="first-collumn" valign="top" style="padding-top: 15px;">
{% if ssh.key_enabled == 1 %}
@ -48,9 +48,9 @@
{{ input(id, value=ssh.username, title='SSH user name') }}
</p>
{% if ssh.key_enabled == 1 %}
<input type="password" id="ssh_pass-{{ssh.id}}" class="form-control" title="User password, if SSH key is disabled" placeholder="*****" style="display: none;" autocomplete="new-password">
<input type="password" id="ssh_pass-{{ssh.id}}" class="form-control" title="User password, if SSH key is disabled" value="{{ ssh.password }}" style="display: none;" autocomplete="new-password">
{% else %}
<input type="password" id="ssh_pass-{{ssh.id}}" class="form-control" title="User password, if SSH key is disabled" placeholder="*****" autocomplete="new-password">
<input type="password" id="ssh_pass-{{ssh.id}}" class="form-control" title="User password, if SSH key is disabled" value="{{ ssh.password }}" autocomplete="new-password">
{% endif %}
<br>
</td>

152
app/views/server/backup_vews.py

@ -8,7 +8,7 @@ import app.modules.db.backup as backup_sql
import app.modules.service.backup as backup_mod
import app.modules.roxywi.common as roxywi_common
from app.middleware import get_user_params, page_for_admin, check_group
from app.modules.roxywi.class_models import BackupRequest, S3BackupRequest, BaseResponse
from app.modules.roxywi.class_models import BackupRequest, S3BackupRequest, GitBackupRequest, BaseResponse
class BackupView(MethodView):
@ -209,7 +209,7 @@ class BackupView(MethodView):
class S3BackupView(MethodView):
methods = ['GET', 'POST', 'PUT', 'DELETE']
methods = ['GET', 'POST', 'DELETE']
decorators = [jwt_required(), get_user_params(), page_for_admin(), check_group()]
def __init__(self, is_api=True):
@ -352,3 +352,151 @@ class S3BackupView(MethodView):
return BaseResponse().model_dump(mode='json'), 204
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot delete S3 backup')
class GitBackupView(MethodView):
methods = ['GET', 'POST', 'DELETE']
decorators = [jwt_required(), get_user_params(), page_for_admin(), check_group()]
def __init__(self, is_api=True):
self.is_api = is_api
@staticmethod
def get(backup_id: int):
"""
Retrieves the details of a specific Git backup configuration.
---
tags:
- Git Backup
parameters:
- in: path
name: backup_id
type: 'integer'
required: true
description: The ID of the specific Git backup
responses:
200:
description: Successful operation
schema:
type: 'object'
properties:
branch:
type: 'string'
description: 'The branch the backup is on'
cred_id:
type: 'integer'
description: 'The ID of the credentials used for the backup'
description:
type: 'string'
description: 'Description for the Git backup configuration'
id:
type: 'integer'
description: 'The ID of the backup'
period:
type: 'string'
description: 'The timing for the Git backup task'
repo:
type: 'string'
description: 'The repository URL for the backup'
server_id:
type: 'integer'
description: 'The ID of the server that was backed up'
service_id:
type: 'integer'
description: 'The service ID of the backup'
default:
description: Unexpected error
"""
try:
backup = backup_sql.get_backup(backup_id, 'git')
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, '')
return jsonify(model_to_dict(backup, recurse=False))
@validate(body=GitBackupRequest)
def post(self, body: GitBackupRequest):
"""
Create a new Git backup.
---
tags:
- Git Backup
parameters:
- name: config
in: body
required: true
description: The configuration for Git backup service
schema:
type: 'object'
properties:
server_id:
type: 'integer'
description: 'The ID of the server to backed up'
service_id:
type: 'integer'
description: 'Service ID'
init:
type: 'integer'
description: 'Indicates whether to initialize the repository'
repo:
type: 'string'
description: 'The repository from where to fetch the data for backup'
branch:
type: 'string'
description: 'The branch to pull for backup'
time:
type: 'string'
description: 'The timing for the Git backup task'
cred_id:
type: 'integer'
description: 'The ID of the credentials to be used for backup'
description:
type: 'string'
description: 'Description for the Git backup configuration'
responses:
201:
description: Successful operation
default:
description: Unexpected error
"""
try:
return backup_mod.create_git_backup(body, self.is_api)
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot create GIT backup')
@validate(body=GitBackupRequest)
def delete(self, backup_id: int, body: GitBackupRequest):
"""
Deletes a specific Git based backup configuration.
---
tags:
- Git Backup
parameters:
- in: path
name: backup_id
type: 'integer'
required: true
description: The ID of the specific Git backup
- name: config
in: body
required: true
description: The configuration for Git backup service delete operation
schema:
type: 'object'
properties:
server_id:
type: 'integer'
description: 'ID of the server from where the backup is to be deleted'
service_id:
type: 'integer'
description: 'Service ID of the backup to be deleted'
responses:
204:
description: Successful operation
default:
description: Unexpected error
"""
try:
return backup_mod.delete_git_backup(body, backup_id)
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot delete GIT backup')

319
app/views/server/cred_views.py

@ -0,0 +1,319 @@
import base64
from flask.views import MethodView
from flask_pydantic import validate
from flask import jsonify, g
from flask_jwt_extended import jwt_required
import app.modules.db.cred as cred_sql
import app.modules.roxywi.common as roxywi_common
import app.modules.server.ssh as ssh_mod
from app.middleware import get_user_params, page_for_admin, check_group
from app.modules.roxywi.exception import RoxywiGroupMismatch, RoxywiResourceNotFound
from app.modules.roxywi.class_models import BaseResponse, GroupQuery, CredRequest, CredUploadRequest
from app.modules.common.common_classes import SupportClass
class CredView(MethodView):
methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']
decorators = [jwt_required(), get_user_params(), page_for_admin(level=2), check_group()]
def __init__(self, is_api=False):
self.is_api = is_api
@staticmethod
@validate(query=GroupQuery)
def get(cred_id: int, query: GroupQuery):
"""
Retrieve credential information for a specific ID
---
tags:
- 'SSH credentials'
parameters:
- in: 'path'
name: 'cred_id'
description: 'ID of the credential to retrieve'
required: true
type: 'integer'
responses:
200:
description: 'Individual Credential Information'
schema:
type: 'object'
properties:
group_id:
type: 'integer'
description: 'Group ID the credential belongs to'
id:
type: 'integer'
description: 'Credential ID'
key_enabled:
type: 'integer'
description: 'Key status of the credential'
name:
type: 'string'
description: 'Name of the credential'
username:
type: 'string'
description: 'Username associated with the credential'
password:
type: 'string'
description: 'Password associated with the credential'
passphrase:
type: 'string'
description: 'Password for the SSH private key'
private_key:
type: 'string'
description: 'SSH private key in base64 encoded format'
404:
description: 'Credential not found'
"""
group_id = SupportClass.return_group_id(query)
try:
creds = ssh_mod.get_creds(group_id=group_id, cred_id=cred_id)
return jsonify(creds), 200
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot get credentials')
@validate(body=CredRequest)
def post(self, body: CredRequest):
"""
Create a new credential entry
---
tags:
- SSH credentials
parameters:
- in: 'path'
name: 'creds_id'
description: 'ID of the credential to retrieve'
required: true
type: 'integer'
- in: body
name: body
schema:
id: AddCredentials
required:
- group_шв
- name
- username
- key_enabled
- password
properties:
group_id:
type: integer
description: The ID of the group to create the credential for. Only for superAdmin role
name:
type: string
description: The credential name
username:
type: string
description: The username
key_enabled:
type: integer
description: If key is enabled or not
password:
type: string
description: The password
responses:
201:
description: Credential addition successful
"""
group_id = SupportClass.return_group_id(body)
try:
return ssh_mod.create_ssh_cred(body.name, body.password, group_id, body.username, body.key_enabled, self.is_api)
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot create new cred')
@validate(body=CredRequest)
def put(self, creds_id: int, body: CredRequest):
"""
Update a credential entry
---
tags:
- SSH credentials
parameters:
- in: 'path'
name: 'creds_id'
description: 'ID of the credential to retrieve'
required: true
type: 'integer'
- in: body
name: body
schema:
id: UpdateCredentials
required:
- name
- username
- key_enabled
- password
properties:
group_id:
type: integer
description: The ID of the group to create the credential for. Only for superAdmin role
name:
type: string
description: The credential name
username:
type: string
description: The username
key_enabled:
type: integer
description: If key is enabled or not
password:
type: string
description: The password
responses:
201:
description: Credential update successful
"""
group_id = SupportClass.return_group_id(body)
try:
self._check_is_correct_group(creds_id)
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, ''), 404
try:
ssh_mod.update_ssh_key(creds_id, body.name, body.password, body.key_enabled, body.username, group_id)
return BaseResponse().model_dump(mode='json'), 201
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot update SSH key')
def delete(self, creds_id: int):
"""
Delete a credential entry
---
tags:
- SSH credentials
parameters:
- in: 'path'
name: 'creds_id'
description: 'ID of the credential to retrieve'
required: true
type: 'integer'
responses:
204:
description: Credential deletion successful
"""
try:
self._check_is_correct_group(creds_id)
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, ''), 404
try:
ssh_mod.delete_ssh_key(creds_id)
return BaseResponse().model_dump(mode='json'), 204
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot delete SSH key')
@validate(body=CredUploadRequest)
def patch(self, creds_id: int, body: CredUploadRequest):
"""
Upload an SSH private key
---
tags:
- SSH credentials
parameters:
- in: 'path'
name: 'creds_id'
description: 'ID of the credential to retrieve'
required: true
type: 'integer'
- in: body
name: body
schema:
id: UploadSSHKey
required:
- private_key
- passphrase
properties:
private_key:
type: string
description: The private key string or base64 encoded string
passphrase:
type: string
description: The passphrase
responses:
201:
description: SSH key upload successful
"""
try:
self._check_is_correct_group(creds_id)
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, ''), 404
try:
body.private_key = base64.b64decode(body.private_key).decode("ascii")
except Exception:
pass
try:
ssh_mod.upload_ssh_key(creds_id, body.private_key, body.passphrase)
return BaseResponse().model_dump(mode='json'), 201
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot upload SSH key')
@staticmethod
def _check_is_correct_group(creds_id: int):
if g.user_params['role'] == 1:
return True
try:
ssh = cred_sql.get_ssh(creds_id)
except RoxywiResourceNotFound:
raise RoxywiResourceNotFound
if ssh.group_id != g.user_params['group_id']:
raise RoxywiGroupMismatch
class CredsView(MethodView):
methods = ['GET']
decorators = [jwt_required(), get_user_params(), page_for_admin(level=2), check_group()]
@validate(query=GroupQuery)
def get(self, query: GroupQuery):
"""
Retrieve credential information based on group_id
---
tags:
- 'SSH credentials'
parameters:
- in: 'query'
name: 'group_id'
description: 'GroupQuery to filter servers. Only for superAdmin role'
required: false
type: 'integer'
responses:
200:
description: 'Credentials Information'
schema:
type: 'array'
items:
type: 'object'
properties:
group_id:
type: 'integer'
description: 'Group ID the credential belongs to'
id:
type: 'integer'
description: 'Credential ID'
key_enabled:
type: 'integer'
description: 'Key status of the credential'
name:
type: 'string'
description: 'Name of the credential'
username:
type: 'string'
description: 'Username of the credential'
password:
type: 'string'
description: 'Password associated with the credential'
passphrase:
type: 'string'
description: 'Password for the SSH private key'
private_key:
type: 'string'
description: 'SSH private key in base64 encoded format'
"""
group_id = SupportClass.return_group_id(query)
try:
creds = ssh_mod.get_creds(group_id=group_id)
return jsonify(creds), 200
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot get credentials')

344
app/views/server/views.py

@ -1,19 +1,17 @@
from flask.views import MethodView
from flask_pydantic import validate
from flask import render_template, jsonify, request, g
from flask import render_template, jsonify, request
from playhouse.shortcuts import model_to_dict
from flask_jwt_extended import jwt_required
from app.modules.db.db_model import Cred
import app.modules.db.cred as cred_sql
import app.modules.db.group as group_sql
import app.modules.db.server as server_sql
import app.modules.roxywi.group as group_mod
import app.modules.roxywi.common as roxywi_common
import app.modules.server.ssh as ssh_mod
import app.modules.server.server as server_mod
from app.middleware import get_user_params, page_for_admin, check_group
from app.modules.roxywi.exception import RoxywiGroupMismatch, RoxywiResourceNotFound
from app.modules.roxywi.exception import RoxywiResourceNotFound
from app.modules.roxywi.class_models import (
BaseResponse, IdResponse, IdDataResponse, ServerRequest, GroupQuery, GroupRequest, CredRequest, CredUploadRequest
)
@ -132,21 +130,20 @@ class ServerView(MethodView):
required:
- hostname
- ip
- enabled
- creds_id
- cred_id
- port
- description
- group_id
properties:
hostname:
type: string
description: The server name
ip:
type: string
description: The server IP address
description: The server IP address or domain name
enabled:
type: integer
description: If server is enabled or not
creds_id:
cred_id:
type: integer
description: The ID of the credentials
port:
@ -158,6 +155,18 @@ class ServerView(MethodView):
group_id:
type: integer
description: The ID of the group to create the server for. Only for superAdmin role
type_ip:
type: integer
description: Is server virtual (VIP address) or not
master:
type: integer
description: Server id of the master server
firewall_enable:
type: integer
description: Is firewalld enabled or not
protected:
type: integer
description: Is the server protected from changes by a non-admin role
responses:
201:
description: Server creation successful
@ -216,19 +225,22 @@ class ServerView(MethodView):
schema:
id: UpdateServer
required:
- name
- enabled
- creds_id
- hostname
- ip
- cred_id
- port
- description
- group_id
properties:
name:
hostname:
type: string
description: The server name
ip:
type: string
description: The server IP or domain name
enabled:
type: integer
description: If server is enabled or not
creds_id:
cred_id:
type: integer
description: The ID of the credentials
port:
@ -240,6 +252,18 @@ class ServerView(MethodView):
group_id:
type: integer
description: The ID of the group to update the server for. Only for superAdmin role
type_ip:
type: integer
description: Is server virtual (VIP address) or not
master:
type: integer
description: Server id of the master server
firewall_enable:
type: integer
description: Is firewalld enabled or not
protected:
type: integer
description: Is the server protected from changes by a non-admin role
responses:
201:
description: Server update successful
@ -248,7 +272,7 @@ class ServerView(MethodView):
try:
server_sql.update_server(
body.hostname, group_id, body.type_ip, body.enabled, body.master, server_id, body.cred_id, body.port, body.description,
body.hostname, body.ip, group_id, body.type_ip, body.enabled, body.master, server_id, body.cred_id, body.port, body.description,
body.firewall_enable, body.protected
)
server_ip = server_sql.select_server_ip_by_id(server_id)
@ -570,294 +594,6 @@ class ServerGroupsView(MethodView):
return jsonify(groups_list)
class CredView(MethodView):
methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']
decorators = [jwt_required(), get_user_params(), page_for_admin(level=2), check_group()]
def __init__(self, is_api=False):
self.is_api = is_api
@staticmethod
def get(creds_id: int):
"""
Retrieve credential information for a specific ID
---
tags:
- 'SSH credentials'
parameters:
- in: 'path'
name: 'creds_id'
description: 'ID of the credential to retrieve'
required: true
type: 'integer'
responses:
200:
description: 'Individual Credential Information'
schema:
type: 'object'
properties:
group_id:
type: 'integer'
description: 'Group ID the credential belongs to'
id:
type: 'integer'
description: 'Credential ID'
key_enabled:
type: 'integer'
description: 'Key status of the credential'
name:
type: 'string'
description: 'Name of the credential'
username:
type: 'string'
description: 'Username associated with the credential'
404:
description: 'Credential not found'
"""
group_id = int(g.user_params['group_id'])
try:
creds = cred_sql.get_ssh_by_id_and_group(creds_id, group_id)
for cred in creds:
return jsonify(model_to_dict(cred, exclude=[Cred.password, Cred.passphrase])), 200
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot get credentials')
@validate(body=CredRequest)
def post(self, body: CredRequest):
"""
Create a new credential entry
---
tags:
- SSH credentials
parameters:
- in: 'path'
name: 'creds_id'
description: 'ID of the credential to retrieve'
required: true
type: 'integer'
- in: body
name: body
schema:
id: AddCredentials
required:
- group_шв
- name
- username
- key_enabled
- password
properties:
group_id:
type: integer
description: The ID of the group to create the credential for. Only for superAdmin role
name:
type: string
description: The credential name
username:
type: string
description: The username
key_enabled:
type: integer
description: If key is enabled or not
password:
type: string
description: The password
responses:
201:
description: Credential addition successful
"""
group_id = SupportClass.return_group_id(body)
try:
return ssh_mod.create_ssh_cred(body.name, body.password, group_id, body.username, body.key_enabled, self.is_api)
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot create new cred')
@validate(body=CredRequest)
def put(self, creds_id: int, body: CredRequest):
"""
Update a credential entry
---
tags:
- SSH credentials
parameters:
- in: 'path'
name: 'creds_id'
description: 'ID of the credential to retrieve'
required: true
type: 'integer'
- in: body
name: body
schema:
id: UpdateCredentials
required:
- name
- username
- key_enabled
- password
properties:
group_id:
type: integer
description: The ID of the group to create the credential for. Only for superAdmin role
name:
type: string
description: The credential name
username:
type: string
description: The username
key_enabled:
type: integer
description: If key is enabled or not
password:
type: string
description: The password
responses:
201:
description: Credential update successful
"""
group_id = SupportClass.return_group_id(body)
try:
self._check_is_correct_group(creds_id)
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, ''), 404
try:
ssh_mod.update_ssh_key(creds_id, body.name, body.password, body.key_enabled, body.username, group_id)
return BaseResponse().model_dump(mode='json'), 201
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot update SSH key')
def delete(self, creds_id: int):
"""
Delete a credential entry
---
tags:
- SSH credentials
parameters:
- in: 'path'
name: 'creds_id'
description: 'ID of the credential to retrieve'
required: true
type: 'integer'
responses:
204:
description: Credential deletion successful
"""
try:
self._check_is_correct_group(creds_id)
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, ''), 404
try:
ssh_mod.delete_ssh_key(creds_id)
return BaseResponse().model_dump(mode='json'), 204
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot delete SSH key')
@validate(body=CredUploadRequest)
def patch(self, creds_id: int, body: CredUploadRequest):
"""
Upload an SSH private key
---
tags:
- SSH credentials
parameters:
- in: 'path'
name: 'creds_id'
description: 'ID of the credential to retrieve'
required: true
type: 'integer'
- in: body
name: body
schema:
id: UploadSSHKey
required:
- private_key
- passphrase
properties:
private_key:
type: string
description: The private key string
passphrase:
type: string
description: The passphrase
responses:
201:
description: SSH key upload successful
"""
try:
self._check_is_correct_group(creds_id)
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, ''), 404
try:
ssh_mod.upload_ssh_key(creds_id, body.private_key, body.passphrase)
return BaseResponse().model_dump(mode='json'), 201
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot upload SSH key')
@staticmethod
def _check_is_correct_group(creds_id: int):
if g.user_params['role'] == 1:
return True
try:
ssh = cred_sql.get_ssh(creds_id)
except RoxywiResourceNotFound:
raise RoxywiResourceNotFound
if ssh.group_id != g.user_params['group_id']:
raise RoxywiGroupMismatch
class CredsView(MethodView):
methods = ['GET']
decorators = [jwt_required(), get_user_params(), page_for_admin(level=2), check_group()]
@validate(query=GroupQuery)
def get(self, query: GroupQuery):
"""
Retrieve credential information based on group_id
---
tags:
- 'SSH credentials'
parameters:
- in: 'query'
name: 'group_id'
description: 'GroupQuery to filter servers. Only for superAdmin role'
required: false
type: 'integer'
responses:
200:
description: 'Credentials Information'
schema:
type: 'array'
items:
type: 'object'
properties:
group_id:
type: 'integer'
description: 'Group ID the credential belongs to'
id:
type: 'integer'
description: 'Credential ID'
key_enabled:
type: 'integer'
description: 'Key status of the credential'
name:
type: 'string'
description: 'Name of the credential'
username:
type: 'string'
description: 'Username of the credential'
"""
group_id = SupportClass.return_group_id(query)
try:
creds = cred_sql.select_ssh(group=group_id)
json_data = []
for cred in creds:
json_data.append(model_to_dict(cred, exclude=[Cred.password, Cred.passphrase]))
return jsonify(json_data), 200
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot get credentials')
class ServerIPView(MethodView):
class ServersView(MethodView):
methods = ["GET"]

1
requirements.txt

@ -12,7 +12,6 @@ slack-sdk>=3.4.0
peewee>=3.14.10
PyMySQL>=1.0.2
distro>=1.2.0
bottle>=0.12.20
psutil>=5.9.1
pdpyras>=4.5.2
pika>=1.3.1

Loading…
Cancel
Save